1. PREPARE

The transition to digital learning has made available new sources of data, providing researchers new opportunities for understanding and improving STEM learning. Data sources such as digital learning environments and administrative data systems, as well as data produced by social media websites and the mass digitization of academic and practitioner publications, hold enormous potential to address a range of pressing problems in STEM Education, but collecting and analyzing text-based data also presents unique challenges.

Text Mining (TM) Module 1: Public Sentiment and the State Standards will help demonstrate how text mining can be applied in STEM education research and provide LASER Institute scholars hands-on experience with popular techniques for collecting, processing, and analyzing text-based data. Specifically, the four learning labs that make up this module address the following topics:

1a. Review the Literature

Text Mining Module 1 is guided by a recent publication by Rosenberg et al. (2020), Advancing new methods for understanding public sentiment about educational reforms: The case of Twitter and the Next Generation Science Standards. This study in turn builds on upon previous work by Wang & Fikis (2017) examining public opinion on the Common Core State Standards (CCSS) on Twitter. For Module 1, we will focus on analyzing tweets about the Next Generation Science Standards (NGSS) and Common Core State Standards (CCSS) in order to better understand key words and phrases that emerge, as well as public sentiment towards these two curriculum reform efforts.

Twitter and the Next Generation Science Standards

Full Paper (Preprint)

Abstract

While the Next Generation Science Standards (NGSS) are a long-standing and widespread standards-based educational reform effort, they have received less public attention, and no studies have explored the sentiment of the views of multiple stakeholders toward them. To establish how public sentiment about this reform might be similar to or different from past efforts, we applied a suite of data science techniques to posts about the standards on Twitter from 2010-2020 (N = 571,378) from 87,719 users. Applying data science techniques to identify teachers and to estimate tweet sentiment, we found that the public sentiment towards the NGSS is overwhelmingly positive – 33 times more so than for the CCSS. Mixed effects models indicated that sentiment became more positive over time and that teachers, in particular, showed a more positive sentiment towards the NGSS. We discuss implications for educational reform efforts and the use of data science methods for understanding their implementation.

Data Sources

Similar to what we’ll be learning in this lab, Rosenberg et al. used publicly accessible data from Twitter collected using the Full-Archive Twitter API and the rtweet package in R. Specifically, the authors accessed tweets and user information from the hashtag-based #NGSSchat online community, all tweets that included any of the following phrases, with “/” indicating an additional phrase featuring the respective plural form: “ngss,” “next generation science standard/s,” “next gen science standard/s.”

Data used in this lab was pulled using an Academic Research developer account and the {academictwitter} package, which uses the Twitter API v2 endpoints and allows researchers to access the full twitter archive. For those that created a standard developer account, the rtweet & the Twitter API supplemental learning lab will show you how to pull your own data from Twitter.

Data for this lab includes all tweets from 2020 that included the following terms: #ccss, common core, #ngsschat, ngss. Below is an example of the code used to retrieve data for this lab. It is set not to run and will not run if you try, but it does illustrate the search query used, variables selected, and time frame.

ccss_tweets_2021 <-
  get_all_tweets('(#commoncore OR "common core") -is:retweet lang:en',
                 "2021-01-01T00:00:00Z",
                 "2021-05-31T00:00:00Z",
                 bearer_token,
                 data_path = "ccss-data/",
                 bind_tweets = FALSE)

ccss_tweets <- bind_tweet_jsons(data_path = "ccss-data/") %>%
  select(text,
         created_at,
         author_id,
         id,
         conversation_id,
         source,
         possibly_sensitive,
         in_reply_to_user_id)


write_csv(ccss_tweets, "data/ccss-tweets.csv")
Analysis

Also similar to what we’ll demonstrate in Lab 3, the authors determined Tweet sentiment using the Java version of SentiStrength to assign tweets to two 5-point scales of sentiment, one for positivity and one for negativity, because SentiStrength is a validated measure for sentiment in short informal texts (Thelwall et al., 2011). In addition, they used this tool because Wang and Fikis (2019) used it to explore the sentiment of CCSS-related posts. We’ll be using the AFINN sentiment lexicon which also assigns words in a tweet to two 5-point scales, in addition to exploring some other sentiment lexicons to see if they produce similar results.

The authors also used the lme4 package in R to run a mixed effects model to determine if sentiment changes over time and differs between teachers and non-teacher. We won’t look at the relationships between tweet sentiment, time and teachers in these labs, but we will take a look at the correlation between words within tweets in TM Learning Lab 2.

Summary of Key Findings

  1. Contrasting with sentiment about CSSS, sentiment about the NGSS science education reform effort is overwhelmingly positive, with approximately 9 positive tweets for every negative tweet.
  2. Teachers were more positive than non-teachers, and sentiment became substantially more positive over the ten years of NGSS-related posts.
  3. Differences between the context of the tweets were small, but those that did not include the #NGSSchat hashtag became more positive over time than those posts that did not include the hashtag.
  4. Individuals posted more tweets during #NGSSchat chats, the sentiment of their posts was more positive, suggesting that while the context of individual tweets has a small effect (with posts not including the hashtag becoming more positive over time), the effect upon individuals of being involved in the #NGSSchat was positive.

Finally, you can watch Dr. Rosenberg provide a quick 3-minute overview of this work at <https://stanford.app.box.com/s/i5ixkj2b8dyy8q5j9o5ww4nafznb497x>

1b. Define Questions

One overarching question that Silge and Robinson (2018) identify as a central question to text mining and natural language processing, and that we’ll explore throughout the text mining labs this year, is the question:

How do we to quantify what a document or collection of documents is about?

The questions guiding the Rosenberg et al. study attempt to quantify public sentiment around the NGSS and how that sentiment changes over time. Specifically, they asked:

  1. What is the public sentiment expressed toward the NGSS?
  2. How does sentiment for teachers differ from non-teachers?
  3. How do tweets posted to #NGSSchat differ from those without the hashtag?
  4. How does participation in #NGSSchat relate to the public sentiment individuals express?
  5. How does public sentiment vary over time?

For our first lab on text mining in STEM education, we’ll use approaches similar to those used by the authors cited above to better understand public discourse surrounding these standards, particularly as they relate to STEM education. We will also try to guage public sentiment around the NGSS, by comparing how much more positive or negative NGSS tweets are relative to CSSS tweets. Specifically, in the next four learning lab we’ll attempt to answer the following questions:

  1. What are the most frequent words or phrases used in reference to tweets about the CCSS and NGSS?
  2. What words and hashtags commonly occur together?
  3. How does sentiment for NGSS compare to sentiment for CCSS?

1c. Load Libraries

tidyverse 📦

As noted in our Getting Started activity, R uses “packages” and add-ons that enhance its functionality. One package that we’ll be using extensively is {tidyverse}. The {tidyverse} package is actually a collection of R packages designed for reading, wrangling, and exploring data and which all share an underlying design philosophy, grammar, and data structures. This shared features are sometimes “tidy data principles.”

Click the green arrow in the right corner of the “code chunk” that follows to load the {tidyverse} library.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.3     ✓ purrr   0.3.4
✓ tibble  3.1.2     ✓ dplyr   1.0.6
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
── Conflicts ─────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Again, don’t worry if you saw a number of messages: those probably mean that the tidyverse loaded just fine. Any conflicts you may have seen mean that functions in these packages you loaded have the same name as functions in other packages and R will default to function from the last loaded package unless you specify otherwise.

tidytext 📦

As we’ll learn first hand in this module, using tidy data principles can also make many text mining tasks easier, more effective, and consistent with tools already in wide use. The {tidytext} package helps to convert text into data frames of individual words, making it easy to to manipulate, summarize, and visualize text using using familiar functions form the {tidyverse} collection of packages.

Let’s go ahead and load the {tidytext} package:

library(tidytext)

For a more comprehensive introduction to the tidytext package, we cannot recommend enough the free online book, Text Mining with R: A Tidy Approach (Silge & Robinson, 2017). If you’re interested in pursuing text analysis using R post Summer Workshop, this will be a go to reference.

2. WRANGLE

The importance of data wrangling, particularly when working with text, is difficult to overstate. Just as a refresher, wrangling involves the initial steps of going from raw data to a dataset that can be explored and modeled (Krumm et al., 2018). Learning Lab 2 will have a heavy emphasis on preparing text for analysis and in particular we’ll learn how to:

  1. Import Data. First we revisit the familiar read_csv() function for reading in our CCSS and NGSS tweets
  2. Restructure Data. We focus on removing extraneous data using the select() and filter() functions from {dplyr}, and introduce two new functions for merging the data frames that we imported.
  3. Tidy Text. We introduce the {tidytext} package to “tidy” and tokenize our tweets in order to create our data frame for analysis revisit the concept of joins to remove “stop words” that don’t add much value to our analysis.

2a. Import and View Data

ccss_tweets <- read_csv("data/ccss-tweets.csv", 
          col_types = cols(author_id = col_character(), 
                           id = col_character(),
                           conversation_id = col_character(), 
                           in_reply_to_user_id = col_character()
                           )
          )

Note the addition of the col_types = argument for changing some of the column types to character strings because the numbers for those particular columns actually indicate identifiers for authors and tweets:

  • author_id = the author of the tweet

  • id = the unique id for each tweet

  • converastion_id = the unique id for each conversation thread

  • in_reply_to_user_id = the author of the tweet being replied to

Your Turn ⤵

RStudio Tip: Importing data and dealing with data types can be a bit tricky, especially for beginners. Fortunately, RStudio has an “Import Dataset” feature in the Environment Pane that can help you use the {readr} package and associated functions to greatly facilitate this process.

Try using the “Import Dataset” feature in the upper right environment pane to import the NGSS tweets located in the data folder.

The code generated should look something like this:

ngss_tweets <- read_csv("data/ngss-tweets.csv", 
          col_types = cols(author_id = col_character(), 
                           id = col_character(),
                           conversation_id = col_character(), 
                           in_reply_to_user_id = col_character()
                           )
          )

Use the following code chunk to inspect your tweets using a function you’ve learned so for for viewing your data:

# your code here

2b. Restructure Data

Subset Tweets

As you may have noticed, we have more data than we need for our analysis and should probably pare it down to just what we’ll use.

Let’s start with the First, since this is a family friendly learning lab, let’s use the filter() function introduced in previous labs to filter out rows containing “possibly sensitive” language:

ccss_tweets_1 <- ccss_tweets %>% 
  filter(possibly_sensitive == "FALSE")

Now let’s use the select() function to select the following columns from our new ss_tweets_clean data frame:

  1. text containing the tweet which is our primary data source of interest
  2. author_id of the user who created the tweet
  3. created_at timestamp for examining changes in sentiment over time
  4. conversation_id for examining sentiment by conversations
  5. id for the unique reference id for each tweet and useful for counts
ccss_tweets_2 <- ccss_tweets_1 %>% 
  select(text,
         author_id,
         created_at, 
         conversation_id,
         id)

Your Turn ⤵

Note: The select() function will also reorder your columns based on the order in which you list them.

Use the code chunk below to reorder the columns to your liking and assign to ccss_tweets_3:

# your code here

Add & Relocate Columns

Finally, since we are interested in comparing the sentiment of NGSS tweets with CSSS tweets, it would be helpful if we had a column to quickly identify the set of state standards with which each tweet is associated.

We’ll use the mutate() function to create a new variable called standards to label each tweets as “ngss”:

ccss_tweets_4 <- mutate(ccss_tweets_2, standards = "ccss")

colnames(ccss_tweets_4)
[1] "text"            "author_id"       "created_at"      "conversation_id" "id"             
[6] "standards"      

And just because it bothers me, I’m going to use the relocate() function to move the standards column to the first position so I can quickly see which standards the tweet is from:

ccss_tweets_5 <- relocate(ccss_tweets_4, standards)

colnames(ccss_tweets_5)
[1] "standards"       "text"            "author_id"       "created_at"      "conversation_id"
[6] "id"             

Again, we could also have used the select() function to reorder columns like so:

ccss_tweets_5 <- ccss_tweets_4 %>% 
  select(standards,
         text,
         author_id,
         created_at, 
         conversation_id,
         id)

colnames(ccss_tweets_5)
[1] "standards"       "text"            "author_id"       "created_at"      "conversation_id"
[6] "id"             

Before moving on to the CCSS standards, let’s use the %>% operator and rewrite the code from our wrangling so there is less redundancy and it is easier to read:

# Search Tweets
ccss_tweets_clean <- ccss_tweets %>%
  filter(possibly_sensitive == "FALSE") %>%
  select(text, author_id, created_at, conversation_id, id) %>%
  mutate(standards = "ccss") %>%
  relocate(standards)

head(ccss_tweets_clean)

Your Turn

Recall from section 1b. Define Questions that we are interested in comparing word usage and public sentiment around both the Common Core and Next Gen Science Standards.

Create an new ngss_tweets_clean data frame consisting of the Next Generation Science Standards tweets we imported by use the code above as a guide.

# your code here

Try not to peek at the answer below unless you are having difficulty with your code.

Answer

ngss_tweets_clean <- ngss_tweets %>%
  filter(possibly_sensitive == "FALSE") %>%
  select(text, author_id, created_at, conversation_id, id) %>%
  mutate(standards = "ngss") %>%
  relocate(standards)

head(ngss_tweets_clean)

Merge Data Frames

Finally, let’s combine our CCSS and NGSS tweets into a single data frame by using the union() function from dplyr and simply supplying the data frames that you want to combine as arguments:

ss_tweets <- union(ccss_tweets_clean,
                   ngss_tweets_clean)

Note that when creating a “union” like this (i.e. stacking one data frame on top of another), you should have the same number of columns in each data frame and they should be in the exact same order.

Your Turn

Finally, let’s take a quick look at both the head() and the tail() of this new ss_tweets data frame to make sure it contains both “ngss” and “ccss” standards:

head(ss_tweets)
tail(ss_tweets)

Wow, so much for a family friendly learning lab! Based on this very limited sample, which set of standards do you think Twitter users are more negative about?

  • your response here

Let’s take a slightly larger sample of the CCSS tweets:

ss_tweets %>% 
  filter(standards == "ccss") %>%
  sample_n(20) %>%
  relocate(text)
NA

Your Turn

Use the code chunk below to take a sample of the NGSS tweets:

# your code here

Still of the same opinion?

  • Respond here…

2c. Tidy Text

Text data by it’s very nature is ESPECIALLY untidy and is sometimes referred to as “unstructured” data. In this section we are introduced to the tidytext package and will learn some new functions to convert text to and from tidy formats. Having our text in a tidy format will allow us to switch seamlessly between tidy tools and existing text mining packages, while also making it easier to visualize text summaries in other data analysis tools like Tableau.

Tokenize Text

In Chapter 1 of Text Mining with R, Silge & Robinson (2017) define the tidy text format as a table with one-token-per-row, and explain that:

A token is a meaningful unit of text, such as a word, two-word phrase (bigram), or sentence that we are interested in using for analysis. And tokenization is the process of splitting text into tokens.

This one-token-per-row structure is in contrast to the ways text is often stored for text analysis, perhaps as strings in a corpus object or in a document-term matrix. For tidy text mining, the token that is stored in each row is most often a single word, but can also be an n-gram, sentence, or paragraph.

For this part of our workflow, our goal is to transform our ss_tweets data from this:

head(relocate(ss_tweets, text))

Into a “tidy text” one-token-per-row format that looks like this:

tidy_tweets <- ss_tweets %>% 
  unnest_tokens(output = word, 
                input = text) %>%
  relocate(word)

head(tidy_tweets)

Later in the year, we’ll learn about other data structures for text analysis like the document-term matrix and corpus objects. For now, however, working with the familiar tidy data frame allows us to take advantage of popular packages that use the shared tidyverse syntax and principles for wrangling, exploring, and modeling data.

Unigrams

As demonstrated above, the tidytext package provides the incredibly powerful unnest_tokens() function to tokenize text (including tweets!) and convert them to a one-token-per-row format.

Let’s tokenize our tweets by using this function to split each tweet into a single row to make it easier to analyze and take a look:

ss_tokens <- unnest_tokens(ss_tweets, 
                             output = word, 
                             input = text)

head(relocate(ss_tokens, word))

There is A LOT to unpack with this function:

  • First notice that unnest_tokens() expects a data frame as the first argument, followed by two column names.
  • The next argument is an output column name that doesn’t currently exist but will be created as the text is “unnested” into it, word in this case).
  • This is followed by the input column that the text comes from, which we uncreatively named text.
  • By default, a token is an individual word or unigram.
  • Other columns, such as author_id and created_at, are retained.
  • All punctuation has been removed.
  • Tokens have been changed to lowercase, which makes them easier to compare or combine with other datasets (use the to_lower = FALSE argument to turn off if desired).

Note: Since {tidytext} follows tidy data principles, we also could have used the %>% operator to pass our data frame to the unnest_tokens() function like so:

ss_tokens <- ss_tweets %>%
  unnest_tokens(output = word, 
                input = text)

Your Turn ⤵

The unnest_tokens() function also has a specialized “tweets” tokenizer in the tokens = argument that is very useful for dealing with Twitter text. It retains hashtags and mentions of usernames with the @ symbol as illustrated by our @catturd2 friend who featured prominently in our the first CCSS tweet.

Rewrite the code above (you can check answer below) to include the token argument set to “tweets,” assign to ss_tokens_1, and answer the questions that follow:

# your code here
  1. How many observations were our original ss_tweets data frame?

  2. How many observations are there now? Why the difference?

Answer

Your code should look something like this:

ss_tokens_1 <- unnest_tokens(ss_tweets, 
                              output = word, 
                              input = text, 
                              token = "tweets")
Using `to_lower = TRUE` with `token = 'tweets'` may not preserve URLs.
head(ss_tokens_1)

Before we move any further let’s take a quick look at the most common word in our two datasets:

ss_tokens_1 %>%
  count(word, sort = TRUE)
NA

Well, many of these tweets are clearly about the ccss and math at least, but beyond that it’s a bit hard to tell because there are so many “stop words” like “the,” “to,” “and,” “in” that don’t carry much meaning by themselves.

Remove Stop Words

Often in text analysis, we will want to remove these stop words if they are not useful for an analysis. The stop_words dataset in the {tidytext} package contains stop words from three lexicons. We can use them all together, as we have here, or filter() to only use one set of stop words if that is more appropriate for a certain analysis.

Let’s take a closer the lexicons and stop words included in each:

View(stop_words)

The anti_join Function

In order to remove these stop words, we will use a function called anti_join() that looks for matching values in a specific column from two datasets and returns rows from the original dataset that have no matches like so:

For a good overview of the different dplyr joins see here: https://medium.com/the-codehub/beginners-guide-to-using-joins-in-r-682fc9b1f119.

Now let’s remove stop words that don’t help us learn much about what people are saying about the state standards.

ss_tokens_2 <- anti_join(ss_tokens_1,
                           stop_words,
                           by = "word")

head(ss_tokens_2)

Notice that we’ve specified the by = argument to look for matching words in the word column for both data sets and remove any rows from the tweet_tokens dataset that match the stop_words dataset. Remember when we first tokenized our dataset I conveniently chose output = word as the column name because it matches the column name word in the stop_words dataset contained in the tidytext package. This makes our call to anti_join()simpler because anti_join() knows to look for the column named word in each dataset. However this wasn’t really necessary since word is the only matching column name in both datasets and it would have matched those columns by default.

Your Turn ⤵

Use the code chunk below to take a quick count of the most common tokens in our ss_tweets_2 data frame to see if the results are a little more meaningful

ss_tokens_2 %>%
  count(word, sort = TRUE)

Custom Stop Words

Notice that the nonsense word “amp” is among our high frequency words as well as some. We can create our own custom stop word list to to weed out any additional words that don’t carry much meaning but skew our data by being so prominent.

Let’s create a custom stop word list by using the simple c() function to combine our words. We can the add a filter to keep rows where words in our word column do NOT ! match words %in% my_stopwords list:

my_stopwords <- c("amp", "=", "+")

ss_tokens_3 <-
  ss_tokens_2 %>%
  filter(!word %in% my_stopwords)

Let’s take a look at our top words again and see if that did the trick:

ss_tokens_3 %>%
  count(word, sort = TRUE)

Much better! Note that we could extend this stop word list indefinitely. Feel free to use the code chunk below to try adding more words to our stop list.

Before we move any further, let’s save our tidied tweets as a new data frame for Section 3 and also save it as a .csv file in our data folder:

ss_tidy_tweets <- ss_tokens_3

write_csv(ss_tokens_3, "data/ss_tidy_tweets.csv")

3. EXPLORE

Calculating summary statistics, data visualization, and feature engineering (the process of creating new variables from a dataset) are a key part of exploratory data analysis. In Section 3, we keep things relatively simple and focus on some simple data summaries:

  1. Word Counts. We focus primarily on the use of word counts and briefly introduce word frequencies to help us identify word commonly used in tweets about the NGSS and CCSS curriculum standards.

  2. Word Frequencies. We wrap up this lab and preview some data visualization work in later labs by creating a simple wordcloud to explore summarize and highlight key words among our tweets.

3a. Word Counts

As highlighted in Word Counts are Amazing, an excellent post and blog by Ted Underwood at University of Illinois, one simple but powerful approach to text analysis is counting the frequency in which words occur in a given collection of documents, or corpus.

Word counts are a good example of a simple approach that illustrates the central question to text mining and natural language processing, introduced at the beginning:

How do we to quantify what a document or collection of documents is about?

So far, we’ve used the count() function from the {dplyr} package to look at word counts across our entire corpus of tweets.

Let’s use the same function to look at counts of the most common words by standards this time since one of our goals is to compare public sentiment between the two standards:

ss_tidy_tweets %>%
  count(standards, word, sort = TRUE)

Note that we included standards in our function to count how often each word occurs for each set of standards. For example, if you tab through the output, you will see that “students” is among the top words in both sets of standards, and occurs 1,432 times in the NGSS tweets and 1,127 times in the CCSS tweets.

Unsurprisingly words from our Twitter API search query are among the top words in each set of standards as well.

It’s a little difficult to directly compare the top words in each set since they are lumped together. Let’s use our filter() function again to just look at the CCSS tweets and save this for later to use in our Reach activity:

ccss_counts <- ss_tidy_tweets %>%
  filter(standards == "ccss") %>%
  count(word, sort = TRUE)

ccss_counts

Your Turn ⤵

Now use the code below to get the counts for our NGSS tweets so we can compare the top words for each set of standards:

# your code here

What might the top words for each set of standards suggest about similarities and differences for how Twitter users talk about each? What might it suggest about public sentiment?

  • your response here

3b. Word Frequencies

We saw above that the word “students” is among the top words in both sets of standards, but to help facilitate comparisons, is often helpful to look at the frequency that each word occurs among all words for that document group. This will also helps us to better gauge how prominent each word is for each set of standards.

For example, let’s create counts for each standards and word paring like we did above, and then create a new column using the mutate() function that calculates the proportion that word makes up among all words:

ccss_frequencies <- ccss_counts %>%
  mutate(proportion = n / sum(n))

ccss_frequencies

Your Turn ⤵

Now use the code below to get the frequencies for our NGSS tweets so we can compare the top words for each set of standards:

# your code here

We can see in both cases that our search terms are heavily skewing our proportions. What might we do to address this?

  • your response here

4. MODEL

As highlighted in Chapter 3 of Data Science in Education Using R , the Model step of the data science process entails “using statistical models, from simple to complex, to understand trends and patterns in the data.” The authors note that while descriptive statistics and data visualization during the Explore step can help us to identify patterns and relationships in our data, statistical models can be used to help us determine if relationships, patterns and trends are actually meaningful. In TM Learning Lab 3, we’ll take a closer look at the study by Rosenberg et al. (2020) and how they used modeling to compare differences in sentiment between teachers and non-teachers discussing the common core.

5. COMMUNICATE

Congratulations - you’ve completed the first text mining learning lab! To complete your work, you can click the drop down arrow at the top of the file, then select “Knit top HTML.” This will create a report in your Files pane that serves as a record of your code and its output you can open or share.

If you wanted, you could save the processed data set to your data folder. The write_csv() function is useful for this. The following code is set to not run, as we wanted to ensure that everyone had the data set needed to begin the second learning lab, but if you’re confident in your prepared data, you can save it with the following:

write_csv()

Reach (Optional)

If you’re using data that you brought to the institute or data that you pulled from Twitter, try tidying your data into a tidy text format and examining the top words in your dataset.

If you’d like to use the data we’ve been working with for your reach, let’s try some basic data visualization with text. The wordcloud2 package is pretty dead simple tool for generating HTML based word clouds.

For example, let’s load the wordclouds2 library, and run the wordcloud2() function on our ccss_counts data frame:

library(wordcloud2)

wordcloud2(ccss_counts)

As you can see, “math” is a pretty common topic with discussing the common core on twitter but words like “core” and “common” are not very helpful since those were in our search terms when pulling data from Twitter.

In a separate r script file, try modifying our list of stop words, retidying our text, and doing a new word count with these and perhaps other words removed. Also, take a look at the help file for wordclouds2 to see if there might be otherwise you could visually improve this visualization.

Word clouds are much maligned and sometimes referred to as pie charts for words, but they can be useful for quickly summarizing and communicating qualitative data for education practitioners and are intuitive for them to interpret. Also, for better or worse, these are now included as a default viz for open-ended survey items in online Qualtrics reports.

Reflection

In this learning lab, we focused on the literature guiding our analysis; wrangling our data into a one-token-per-row tidy text format; and using simple word counts and frequencies to compare common words used in tweets about the NGSS and CCSS curriculum standards. Below, add a few notes in response to the following prompts:

  1. One thing I took away from this learning lab:

  2. One thing I want to learn more about:

References

Note: Citations embedded in R Markdown will only show upon knitting.

Krumm, A., Means, B., & Bienkowski, M. (2018). Learning analytics goes to school. Routledge. https://doi.org/10.4324/9781315650722
Rosenberg, J., Borchers, C., Dyer, E. B., Anderson, D., & Fischer, C. (2020). Understanding public sentiment about educational reforms: The next generation science standards on twitter. http://dx.doi.org/10.31219/osf.io/xymsd
Silge, J., & Robinson, D. (2017). Text mining with r: A tidy approach. " O’Reilly Media, Inc.". https://www.tidytextmining.com
Wang, Y., & Fikis, D. J. (2017). Common Core State Standards on Twitter: Public Sentiment and Opinion Leaders. Educational Policy, 33(4), 650–683. https://doi.org/10.1177/0895904817723739
LS0tCnRpdGxlOiAnVGlkeSBUZXh0LCBUb2tlbnMsIGFuZCBUd2l0dGVyJwpzdWJ0aXRsZTogJ1RleHQgTWluaW5nIExlYXJuaW5nIExhYiAxJwphdXRob3I6ICJZT1VSIE5BTUUgSEVSRSIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCclQiAlZSwgJVknKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9ub3RlYm9vazoKYmlibGlvZ3JhcGh5OiBsaXQvcmVmZXJlbmNlcy5iaWIKY3NsOiBsaXQvYXBhLmNzbAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyMgMS4gUFJFUEFSRQoKVGhlIHRyYW5zaXRpb24gdG8gZGlnaXRhbCBsZWFybmluZyBoYXMgbWFkZSBhdmFpbGFibGUgbmV3IHNvdXJjZXMgb2YgZGF0YSwgcHJvdmlkaW5nIHJlc2VhcmNoZXJzIG5ldyBvcHBvcnR1bml0aWVzIGZvciB1bmRlcnN0YW5kaW5nIGFuZCBpbXByb3ZpbmcgU1RFTSBsZWFybmluZy4gRGF0YSBzb3VyY2VzIHN1Y2ggYXMgZGlnaXRhbCBsZWFybmluZyBlbnZpcm9ubWVudHMgYW5kIGFkbWluaXN0cmF0aXZlIGRhdGEgc3lzdGVtcywgYXMgd2VsbCBhcyBkYXRhIHByb2R1Y2VkIGJ5IHNvY2lhbCBtZWRpYSB3ZWJzaXRlcyBhbmQgdGhlIG1hc3MgZGlnaXRpemF0aW9uIG9mIGFjYWRlbWljIGFuZCBwcmFjdGl0aW9uZXIgcHVibGljYXRpb25zLCBob2xkIGVub3Jtb3VzIHBvdGVudGlhbCB0byBhZGRyZXNzIGEgcmFuZ2Ugb2YgcHJlc3NpbmcgcHJvYmxlbXMgaW4gU1RFTSBFZHVjYXRpb24sIGJ1dCBjb2xsZWN0aW5nIGFuZCBhbmFseXppbmcgdGV4dC1iYXNlZCBkYXRhIGFsc28gcHJlc2VudHMgdW5pcXVlIGNoYWxsZW5nZXMuCgoqKlRleHQgTWluaW5nIChUTSkgTW9kdWxlIDE6IFB1YmxpYyBTZW50aW1lbnQgYW5kIHRoZSBTdGF0ZSBTdGFuZGFyZHMqKiB3aWxsIGhlbHAgZGVtb25zdHJhdGUgaG93IHRleHQgbWluaW5nIGNhbiBiZSBhcHBsaWVkIGluIFNURU0gZWR1Y2F0aW9uIHJlc2VhcmNoIGFuZCBwcm92aWRlIExBU0VSIEluc3RpdHV0ZSBzY2hvbGFycyBoYW5kcy1vbiBleHBlcmllbmNlIHdpdGggcG9wdWxhciB0ZWNobmlxdWVzIGZvciBjb2xsZWN0aW5nLCBwcm9jZXNzaW5nLCBhbmQgYW5hbHl6aW5nIHRleHQtYmFzZWQgZGF0YS4gU3BlY2lmaWNhbGx5LCB0aGUgZm91ciBsZWFybmluZyBsYWJzIHRoYXQgbWFrZSB1cCB0aGlzIG1vZHVsZSBhZGRyZXNzIHRoZSBmb2xsb3dpbmcgdG9waWNzOgoKLSAgICoqTGVhcm5pbmcgTGFiIDE6IFRpZHkgVGV4dCwgVG9rZW5zLCAmIFR3aXR0ZXIuKirCoFdlIHRha2UgYSBjbG9zZXIgbG9vayBhdCB0aGUgbGl0ZXJhdHVyZSBndWlkaW5nIG91ciBhbmFseXNpczsgd3JhbmdsZSBvdXIgZGF0YSBpbnRvIGEgb25lLXRva2VuLXBlci1yb3cgdGlkeSB0ZXh0IGZvcm1hdDsgYW5kIHVzZSBzaW1wbGUgd29yZCBjb3VudHMgdG8gZXhwbG9yZSBvdXIgdHdlZXRzIGFib3V0IHRoZSBjb21tb24gY29yZSBhbmQgbmV4dCBnZW5lcmF0aW9uIHNjaWVuY2Ugc3RhbmRhcmRzLgoKLSAgICoqTGVhcm5pbmcgTGFiIDI6IFR3aWNlIHRoZSBmdW4gd2l0aCBCaWdyYW1zLioqwqBGb3Igb3VyIHNlY29uZCBsYWIsIHdlIGV4cGxvcmUgb3VyIHVuaWdyYW1zLCBvciBzaW5nbGUgd29yZCB0b2tlbnMgYSBsaXR0bGUgbW9yZSwgYW5kIGFsc28gc2VlIHdoYXQgcGFpcnMgb2Ygd29yZHMgYW5kIHdvcmQgY29ycmVsYXRpb25zIHRlbGwgdXMgYWJvdXQgb3VyIHR3ZWV0cyB3aGF0IGluc2lnaHQgdGhleSBwcm92aWRlIGluIHJlc3BvbnNlIHRvIG91ciByZXNlYXJjaCBxdWVzdGlvbnMuCgotICAgKipMZWFybmluZyBMYWIgMzogQ29tZSB0byB0aGUgRGFyayBTaWRlLioqwqBXZSBmb2N1cyBvbiB0aGUgdXNlIG9mIGxleGljb25zIGluIG91ciB0aGlyZCBsYWIgYW5kIGludHJvZHVjZSB0aGUge3ZhZGVyfSBwYWNrYWdlIHRvIGNvbXBhcmUgdGhlIHNlbnRpbWVudCBvZiB0d2VldHMgYWJvdXQgdGhlIE5HU1MgYW5kIENDU1Mgc3RhdGUgc3RhbmRhcmRzIGluIG9yZGVyIHRvIGJldHRlciB1bmRlcnN0YW5kIHB1YmxpYyByZWFjdGlvbiB0byB0aGVzZSB0d28gY3VycmljdWx1bSByZWZvcm0gZWZmb3J0cy7CoAoKLSAgICoqTGVhcm5pbmcgTGFiIDQ6IEEgVGFsZSBvZiBUd28gU3RhbmRhcmRzLioqIFdlIHdyYXAgb3VyIGxvb2sgYXQgcHVibGljIHNlbnRpbWVudCBhcm91bmQgU1RFTSBzdGF0ZSBjdXJyaWN1bHVtIHN0YW5kYXJkcyBieSBzZWxlY3RpbmcgYW4gYW5hbHlzaXMgdGhhdCBwcm92aWRlcyBzb21lIHVuaXF1ZSBpbnNpZ2h0OyByZWZpbmluZyBhbmQgcG9saXNoaW5nIGEgZGF0YSBwcm9kdWN0OyBhbmQgd3JpdGluZyBhIGJyaWVmIG5hcnJhdGl2ZSB0byBjb21tdW5pY2F0ZSBmaW5kaW5ncyBpbiByZXNwb25zZSB0byBvdXIgcmVzZWFyY2ggcXVlc3Rpb25zLgoKIyMjIDFhLiBSZXZpZXcgdGhlIExpdGVyYXR1cmUKClRleHQgTWluaW5nIE1vZHVsZSAxIGlzIGd1aWRlZCBieSBhIHJlY2VudCBwdWJsaWNhdGlvbiBieSBAcm9zZW5iZXJnMjAyMCwgKkFkdmFuY2luZyBuZXcgbWV0aG9kcyBmb3IgdW5kZXJzdGFuZGluZyBwdWJsaWMgc2VudGltZW50IGFib3V0IGVkdWNhdGlvbmFsIHJlZm9ybXM6IFRoZSBjYXNlIG9mIFR3aXR0ZXIgYW5kIHRoZSBOZXh0IEdlbmVyYXRpb24gU2NpZW5jZSBTdGFuZGFyZHMqLiBUaGlzIHN0dWR5IGluIHR1cm4gYnVpbGRzIG9uIHVwb24gcHJldmlvdXMgd29yayBieSBAd2FuZzIwMTcgZXhhbWluaW5nIHB1YmxpYyBvcGluaW9uIG9uIHRoZSBDb21tb24gQ29yZSBTdGF0ZSBTdGFuZGFyZHMgKENDU1MpIG9uIFR3aXR0ZXIuIEZvciBNb2R1bGUgMSwgd2Ugd2lsbCBmb2N1cyBvbiBhbmFseXppbmcgdHdlZXRzIGFib3V0IHRoZSBbTmV4dCBHZW5lcmF0aW9uIFNjaWVuY2UgU3RhbmRhcmRzXShodHRwczovL3d3dy5uZXh0Z2Vuc2NpZW5jZS5vcmcpIChOR1NTKSBhbmQgW0NvbW1vbiBDb3JlIFN0YXRlIFN0YW5kYXJkc10oaHR0cDovL3d3dy5jb3Jlc3RhbmRhcmRzLm9yZykgKENDU1MpIGluIG9yZGVyIHRvIGJldHRlciB1bmRlcnN0YW5kIGtleSB3b3JkcyBhbmQgcGhyYXNlcyB0aGF0IGVtZXJnZSwgYXMgd2VsbCBhcyBwdWJsaWMgc2VudGltZW50IHRvd2FyZHMgdGhlc2UgdHdvIGN1cnJpY3VsdW0gcmVmb3JtIGVmZm9ydHMuCgojIyMjIFR3aXR0ZXIgYW5kIHRoZSBOZXh0IEdlbmVyYXRpb24gU2NpZW5jZSBTdGFuZGFyZHMKCiFbXShpbWcvcm9zZW5iZXJnLnBuZyl7d2lkdGg9IjMwJSJ9CgpbRnVsbCBQYXBlciAoUHJlcHJpbnQpXShodHRwczovL29zZi5pby94eW1zZC8uKQoKIyMjIyMgKipBYnN0cmFjdCoqCgpXaGlsZSB0aGUgTmV4dCBHZW5lcmF0aW9uIFNjaWVuY2UgU3RhbmRhcmRzIChOR1NTKSBhcmUgYSBsb25nLXN0YW5kaW5nIGFuZCB3aWRlc3ByZWFkIHN0YW5kYXJkcy1iYXNlZCBlZHVjYXRpb25hbCByZWZvcm0gZWZmb3J0LCB0aGV5IGhhdmUgcmVjZWl2ZWQgbGVzcyBwdWJsaWMgYXR0ZW50aW9uLCBhbmQgbm8gc3R1ZGllcyBoYXZlIGV4cGxvcmVkIHRoZSBzZW50aW1lbnQgb2YgdGhlIHZpZXdzIG9mIG11bHRpcGxlIHN0YWtlaG9sZGVycyB0b3dhcmQgdGhlbS4gVG8gZXN0YWJsaXNoIGhvdyBwdWJsaWMgc2VudGltZW50IGFib3V0IHRoaXMgcmVmb3JtIG1pZ2h0IGJlIHNpbWlsYXIgdG8gb3IgZGlmZmVyZW50IGZyb20gcGFzdCBlZmZvcnRzLCB3ZSBhcHBsaWVkIGEgc3VpdGUgb2YgZGF0YSBzY2llbmNlIHRlY2huaXF1ZXMgdG8gcG9zdHMgYWJvdXQgdGhlIHN0YW5kYXJkcyBvbiBUd2l0dGVyIGZyb20gMjAxMC0yMDIwIChOID0gNTcxLDM3OCkgZnJvbSA4Nyw3MTkgdXNlcnMuIEFwcGx5aW5nIGRhdGEgc2NpZW5jZSB0ZWNobmlxdWVzIHRvIGlkZW50aWZ5IHRlYWNoZXJzIGFuZCB0byBlc3RpbWF0ZSB0d2VldCBzZW50aW1lbnQsIHdlIGZvdW5kIHRoYXQgdGhlIHB1YmxpYyBzZW50aW1lbnQgdG93YXJkcyB0aGUgTkdTUyBpcyBvdmVyd2hlbG1pbmdseSBwb3NpdGl2ZSAtLSAzMyB0aW1lcyBtb3JlIHNvIHRoYW4gZm9yIHRoZSBDQ1NTLiBNaXhlZCBlZmZlY3RzIG1vZGVscyBpbmRpY2F0ZWQgdGhhdCBzZW50aW1lbnQgYmVjYW1lIG1vcmUgcG9zaXRpdmUgb3ZlciB0aW1lIGFuZCB0aGF0IHRlYWNoZXJzLCBpbiBwYXJ0aWN1bGFyLCBzaG93ZWQgYSBtb3JlIHBvc2l0aXZlIHNlbnRpbWVudCB0b3dhcmRzIHRoZSBOR1NTLiBXZSBkaXNjdXNzIGltcGxpY2F0aW9ucyBmb3IgZWR1Y2F0aW9uYWwgcmVmb3JtIGVmZm9ydHMgYW5kIHRoZSB1c2Ugb2YgZGF0YSBzY2llbmNlIG1ldGhvZHMgZm9yIHVuZGVyc3RhbmRpbmcgdGhlaXIgaW1wbGVtZW50YXRpb24uCgojIyMjIyAqKkRhdGEgU291cmNlcyoqCgpTaW1pbGFyIHRvIHdoYXQgd2UnbGwgYmUgbGVhcm5pbmcgaW4gdGhpcyBsYWIsIFJvc2VuYmVyZyBldCBhbC4gdXNlZCBwdWJsaWNseSBhY2Nlc3NpYmxlIGRhdGEgZnJvbSBUd2l0dGVyIGNvbGxlY3RlZCB1c2luZyB0aGUgRnVsbC1BcmNoaXZlIFR3aXR0ZXIgQVBJIGFuZCB0aGUgYHJ0d2VldGAgcGFja2FnZSBpbiBSLiBTcGVjaWZpY2FsbHksIHRoZSBhdXRob3JzIGFjY2Vzc2VkIHR3ZWV0cyBhbmQgdXNlciBpbmZvcm1hdGlvbiBmcm9tIHRoZSBoYXNodGFnLWJhc2VkIFwjTkdTU2NoYXQgb25saW5lIGNvbW11bml0eSwgYWxsIHR3ZWV0cyB0aGF0IGluY2x1ZGVkIGFueSBvZiB0aGUgZm9sbG93aW5nIHBocmFzZXMsIHdpdGggIi8iIGluZGljYXRpbmcgYW4gYWRkaXRpb25hbCBwaHJhc2UgZmVhdHVyaW5nIHRoZSByZXNwZWN0aXZlIHBsdXJhbCBmb3JtOiAibmdzcyIsICJuZXh0IGdlbmVyYXRpb24gc2NpZW5jZSBzdGFuZGFyZC9zIiwgIm5leHQgZ2VuIHNjaWVuY2Ugc3RhbmRhcmQvcyIuCgpEYXRhIHVzZWQgaW4gdGhpcyBsYWIgd2FzIHB1bGxlZCB1c2luZyBhbiBbQWNhZGVtaWMgUmVzZWFyY2ggZGV2ZWxvcGVyIGFjY291bnRdKGh0dHBzOi8vZGV2ZWxvcGVyLnR3aXR0ZXIuY29tL2VuL3Byb2R1Y3RzL3R3aXR0ZXItYXBpL2FjYWRlbWljLXJlc2VhcmNoKSBhbmQgdGhlIHthY2FkZW1pY3R3aXR0ZXJ9IHBhY2thZ2UsIHdoaWNoIHVzZXMgdGhlIFR3aXR0ZXIgQVBJIHYyIGVuZHBvaW50cyBhbmQgYWxsb3dzIHJlc2VhcmNoZXJzIHRvIGFjY2VzcyB0aGUgZnVsbCB0d2l0dGVyIGFyY2hpdmUuIEZvciB0aG9zZSB0aGF0IGNyZWF0ZWQgYSBzdGFuZGFyZCBkZXZlbG9wZXIgYWNjb3VudCwgdGhlIFtydHdlZXQgJiB0aGUgVHdpdHRlciBBUEldKGh0dHBzOi8vbGFzZXItaW5zdGl0dXRlLmdpdGh1Yi5pby90ZXh0LW1pbmluZy90bS1sZWFybmluZy1sYWItMWEuaHRtbCkgc3VwcGxlbWVudGFsIGxlYXJuaW5nIGxhYiB3aWxsIHNob3cgeW91IGhvdyB0byBwdWxsIHlvdXIgb3duIGRhdGEgZnJvbSBUd2l0dGVyLgoKRGF0YSBmb3IgdGhpcyBsYWIgaW5jbHVkZXMgYWxsIHR3ZWV0cyBmcm9tIDIwMjAgdGhhdCBpbmNsdWRlZCB0aGUgZm9sbG93aW5nIHRlcm1zOiBgI2Njc3NgLCBgY29tbW9uIGNvcmVgLCBgI25nc3NjaGF0YCwgYG5nc3NgLiBCZWxvdyBpcyBhbiBleGFtcGxlIG9mIHRoZSBjb2RlIHVzZWQgdG8gcmV0cmlldmUgZGF0YSBmb3IgdGhpcyBsYWIuIEl0IGlzIHNldCBub3QgdG8gcnVuIGFuZCB3aWxsIG5vdCBydW4gaWYgeW91IHRyeSwgYnV0IGl0IGRvZXMgaWxsdXN0cmF0ZSB0aGUgc2VhcmNoIHF1ZXJ5IHVzZWQsIHZhcmlhYmxlcyBzZWxlY3RlZCwgYW5kIHRpbWUgZnJhbWUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KY2Nzc190d2VldHNfMjAyMSA8LQogIGdldF9hbGxfdHdlZXRzKCcoI2NvbW1vbmNvcmUgT1IgImNvbW1vbiBjb3JlIikgLWlzOnJldHdlZXQgbGFuZzplbicsCiAgICAgICAgICAgICAgICAgIjIwMjEtMDEtMDFUMDA6MDA6MDBaIiwKICAgICAgICAgICAgICAgICAiMjAyMS0wNS0zMVQwMDowMDowMFoiLAogICAgICAgICAgICAgICAgIGJlYXJlcl90b2tlbiwKICAgICAgICAgICAgICAgICBkYXRhX3BhdGggPSAiY2Nzcy1kYXRhLyIsCiAgICAgICAgICAgICAgICAgYmluZF90d2VldHMgPSBGQUxTRSkKCmNjc3NfdHdlZXRzIDwtIGJpbmRfdHdlZXRfanNvbnMoZGF0YV9wYXRoID0gImNjc3MtZGF0YS8iKSAlPiUKICBzZWxlY3QodGV4dCwKICAgICAgICAgY3JlYXRlZF9hdCwKICAgICAgICAgYXV0aG9yX2lkLAogICAgICAgICBpZCwKICAgICAgICAgY29udmVyc2F0aW9uX2lkLAogICAgICAgICBzb3VyY2UsCiAgICAgICAgIHBvc3NpYmx5X3NlbnNpdGl2ZSwKICAgICAgICAgaW5fcmVwbHlfdG9fdXNlcl9pZCkKCgp3cml0ZV9jc3YoY2Nzc190d2VldHMsICJkYXRhL2Njc3MtdHdlZXRzLmNzdiIpCmBgYAoKIyMjIyMgKipBbmFseXNpcyoqCgpBbHNvIHNpbWlsYXIgdG8gd2hhdCB3ZSdsbCBkZW1vbnN0cmF0ZSBpbiBMYWIgMywgdGhlIGF1dGhvcnMgZGV0ZXJtaW5lZCBUd2VldCBzZW50aW1lbnQgdXNpbmcgdGhlIEphdmEgdmVyc2lvbiBvZiBTZW50aVN0cmVuZ3RoIHRvIGFzc2lnbiB0d2VldHMgdG8gdHdvIDUtcG9pbnQgc2NhbGVzIG9mIHNlbnRpbWVudCwgb25lIGZvciBwb3NpdGl2aXR5IGFuZCBvbmUgZm9yIG5lZ2F0aXZpdHksIGJlY2F1c2UgU2VudGlTdHJlbmd0aCBpcyBhIHZhbGlkYXRlZCBtZWFzdXJlIGZvciBzZW50aW1lbnQgaW4gc2hvcnQgaW5mb3JtYWwgdGV4dHMgKFRoZWx3YWxsIGV0IGFsLiwgMjAxMSkuIEluIGFkZGl0aW9uLCB0aGV5IHVzZWQgdGhpcyB0b29sIGJlY2F1c2UgV2FuZyBhbmQgRmlraXMgKDIwMTkpIHVzZWQgaXQgdG8gZXhwbG9yZSB0aGUgc2VudGltZW50IG9mIENDU1MtcmVsYXRlZCBwb3N0cy4gV2UnbGwgYmUgdXNpbmcgdGhlIEFGSU5OIHNlbnRpbWVudCBsZXhpY29uIHdoaWNoIGFsc28gYXNzaWducyB3b3JkcyBpbiBhIHR3ZWV0IHRvIHR3byA1LXBvaW50IHNjYWxlcywgaW4gYWRkaXRpb24gdG8gZXhwbG9yaW5nIHNvbWUgb3RoZXIgc2VudGltZW50IGxleGljb25zIHRvIHNlZSBpZiB0aGV5IHByb2R1Y2Ugc2ltaWxhciByZXN1bHRzLgoKVGhlIGF1dGhvcnMgYWxzbyB1c2VkIHRoZSBgbG1lNGAgcGFja2FnZSBpbiBSIHRvIHJ1biBhIG1peGVkIGVmZmVjdHMgbW9kZWwgdG8gZGV0ZXJtaW5lIGlmIHNlbnRpbWVudCBjaGFuZ2VzIG92ZXIgdGltZSBhbmQgZGlmZmVycyBiZXR3ZWVuIHRlYWNoZXJzIGFuZCBub24tdGVhY2hlci4gV2Ugd29uJ3QgbG9vayBhdCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHR3ZWV0IHNlbnRpbWVudCwgdGltZSBhbmQgdGVhY2hlcnMgaW4gdGhlc2UgbGFicywgYnV0IHdlIHdpbGwgdGFrZSBhIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gd29yZHMgd2l0aGluIHR3ZWV0cyBpbiBUTSBMZWFybmluZyBMYWIgMi4KCioqU3VtbWFyeSBvZiBLZXkgRmluZGluZ3MqKgoKMS4gIENvbnRyYXN0aW5nIHdpdGggc2VudGltZW50IGFib3V0IENTU1MsIHNlbnRpbWVudCBhYm91dCB0aGUgTkdTUyBzY2llbmNlIGVkdWNhdGlvbiByZWZvcm0gZWZmb3J0IGlzIG92ZXJ3aGVsbWluZ2x5IHBvc2l0aXZlLCB3aXRoIGFwcHJveGltYXRlbHkgOSBwb3NpdGl2ZSB0d2VldHMgZm9yIGV2ZXJ5IG5lZ2F0aXZlIHR3ZWV0LgoyLiAgVGVhY2hlcnMgd2VyZSBtb3JlIHBvc2l0aXZlIHRoYW4gbm9uLXRlYWNoZXJzLCBhbmQgc2VudGltZW50IGJlY2FtZSBzdWJzdGFudGlhbGx5IG1vcmUgcG9zaXRpdmUgb3ZlciB0aGUgdGVuIHllYXJzIG9mIE5HU1MtcmVsYXRlZCBwb3N0cy4KMy4gIERpZmZlcmVuY2VzIGJldHdlZW4gdGhlIGNvbnRleHQgb2YgdGhlIHR3ZWV0cyB3ZXJlIHNtYWxsLCBidXQgdGhvc2UgdGhhdCBkaWQgbm90IGluY2x1ZGUgdGhlIFwjTkdTU2NoYXQgaGFzaHRhZyBiZWNhbWUgbW9yZSBwb3NpdGl2ZSBvdmVyIHRpbWUgdGhhbiB0aG9zZSBwb3N0cyB0aGF0IGRpZCBub3QgaW5jbHVkZSB0aGUgaGFzaHRhZy4KNC4gIEluZGl2aWR1YWxzIHBvc3RlZCBtb3JlIHR3ZWV0cyBkdXJpbmcgXCNOR1NTY2hhdCBjaGF0cywgdGhlIHNlbnRpbWVudCBvZiB0aGVpciBwb3N0cyB3YXMgbW9yZSBwb3NpdGl2ZSwgc3VnZ2VzdGluZyB0aGF0IHdoaWxlIHRoZSBjb250ZXh0IG9mIGluZGl2aWR1YWwgdHdlZXRzIGhhcyBhIHNtYWxsIGVmZmVjdCAod2l0aCBwb3N0cyBub3QgaW5jbHVkaW5nIHRoZSBoYXNodGFnIGJlY29taW5nIG1vcmUgcG9zaXRpdmUgb3ZlciB0aW1lKSwgdGhlIGVmZmVjdCB1cG9uIGluZGl2aWR1YWxzIG9mIGJlaW5nIGludm9sdmVkIGluIHRoZSBcI05HU1NjaGF0IHdhcyBwb3NpdGl2ZS4KCkZpbmFsbHksIHlvdSBjYW4gd2F0Y2ggRHIuIFJvc2VuYmVyZyBwcm92aWRlIGEgcXVpY2sgMy1taW51dGUgb3ZlcnZpZXcgb2YgdGhpcyB3b3JrIGF0IFtcPGh0dHBzOi8vc3RhbmZvcmQuYXBwLmJveC5jb20vcy9pNWl4a2oyYjhkeXk4cTVqOW81d3c0bmFmem5iNDk3eFw+XShodHRwczovL3N0YW5mb3JkLmFwcC5ib3guY29tL3MvaTVpeGtqMmI4ZHl5OHE1ajlvNXd3NG5hZnpuYjQ5N3gpey51cml9CgojIyMgMWIuIERlZmluZSBRdWVzdGlvbnMKCk9uZSBvdmVyYXJjaGluZyBxdWVzdGlvbiB0aGF0IFNpbGdlIGFuZCBSb2JpbnNvbiAoMjAxOCkgaWRlbnRpZnkgYXMgYSBjZW50cmFsIHF1ZXN0aW9uIHRvIHRleHQgbWluaW5nIGFuZCBuYXR1cmFsIGxhbmd1YWdlIHByb2Nlc3NpbmcsIGFuZCB0aGF0IHdlJ2xsIGV4cGxvcmUgdGhyb3VnaG91dCB0aGUgdGV4dCBtaW5pbmcgbGFicyB0aGlzIHllYXIsIGlzIHRoZSBxdWVzdGlvbjoKCj4gSG93IGRvIHdlIHRvICoqcXVhbnRpZnkqKiB3aGF0IGEgZG9jdW1lbnQgb3IgY29sbGVjdGlvbiBvZiBkb2N1bWVudHMgaXMgYWJvdXQ/CgpUaGUgcXVlc3Rpb25zIGd1aWRpbmcgdGhlIFJvc2VuYmVyZyBldCBhbC4gc3R1ZHkgYXR0ZW1wdCB0byBxdWFudGlmeSBwdWJsaWMgc2VudGltZW50IGFyb3VuZCB0aGUgTkdTUyBhbmQgaG93IHRoYXQgc2VudGltZW50IGNoYW5nZXMgb3ZlciB0aW1lLiBTcGVjaWZpY2FsbHksIHRoZXkgYXNrZWQ6CgoxLiAgV2hhdCBpcyB0aGUgcHVibGljIHNlbnRpbWVudCBleHByZXNzZWQgdG93YXJkIHRoZSBOR1NTPwoyLiAgSG93IGRvZXMgc2VudGltZW50IGZvciB0ZWFjaGVycyBkaWZmZXIgZnJvbSBub24tdGVhY2hlcnM/CjMuICBIb3cgZG8gdHdlZXRzIHBvc3RlZCB0byBcI05HU1NjaGF0IGRpZmZlciBmcm9tIHRob3NlIHdpdGhvdXQgdGhlIGhhc2h0YWc/CjQuICBIb3cgZG9lcyBwYXJ0aWNpcGF0aW9uIGluIFwjTkdTU2NoYXQgcmVsYXRlIHRvIHRoZSBwdWJsaWMgc2VudGltZW50IGluZGl2aWR1YWxzIGV4cHJlc3M/CjUuICBIb3cgZG9lcyBwdWJsaWMgc2VudGltZW50IHZhcnkgb3ZlciB0aW1lPwoKRm9yIG91ciBmaXJzdCBsYWIgb24gdGV4dCBtaW5pbmcgaW4gU1RFTSBlZHVjYXRpb24sIHdlJ2xsIHVzZSBhcHByb2FjaGVzIHNpbWlsYXIgdG8gdGhvc2UgdXNlZCBieSB0aGUgYXV0aG9ycyBjaXRlZCBhYm92ZSB0byBiZXR0ZXIgdW5kZXJzdGFuZCBwdWJsaWMgZGlzY291cnNlIHN1cnJvdW5kaW5nIHRoZXNlIHN0YW5kYXJkcywgcGFydGljdWxhcmx5IGFzIHRoZXkgcmVsYXRlIHRvIFNURU0gZWR1Y2F0aW9uLiBXZSB3aWxsIGFsc28gdHJ5IHRvIGd1YWdlIHB1YmxpYyBzZW50aW1lbnQgYXJvdW5kIHRoZSBOR1NTLCBieSBjb21wYXJpbmcgaG93IG11Y2ggbW9yZSBwb3NpdGl2ZSBvciBuZWdhdGl2ZSBOR1NTIHR3ZWV0cyBhcmUgcmVsYXRpdmUgdG8gQ1NTUyB0d2VldHMuIFNwZWNpZmljYWxseSwgaW4gdGhlIG5leHQgZm91ciBsZWFybmluZyBsYWIgd2UnbGwgYXR0ZW1wdCB0byBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6CgoxLiAgV2hhdCBhcmUgdGhlIG1vc3QgZnJlcXVlbnQgd29yZHMgb3IgcGhyYXNlcyB1c2VkIGluIHJlZmVyZW5jZSB0byB0d2VldHMgYWJvdXQgdGhlIENDU1MgYW5kIE5HU1M/CjIuICBXaGF0IHdvcmRzIGFuZCBoYXNodGFncyBjb21tb25seSBvY2N1ciB0b2dldGhlcj8KMy4gIEhvdyBkb2VzIHNlbnRpbWVudCBmb3IgTkdTUyBjb21wYXJlIHRvIHNlbnRpbWVudCBmb3IgQ0NTUz8KCiMjIyAxYy4gTG9hZCBMaWJyYXJpZXMKCiMjIyMgdGlkeXZlcnNlIPCfk6YKCiFbXShpbWcvdGlkeXZlcnNlLnBuZyl7d2lkdGg9IjIwJSJ9CgpBcyBub3RlZCBpbiBvdXIgR2V0dGluZyBTdGFydGVkIGFjdGl2aXR5LCBSIHVzZXMgInBhY2thZ2VzIiBhbmQgYWRkLW9ucyB0aGF0IGVuaGFuY2UgaXRzIGZ1bmN0aW9uYWxpdHkuIE9uZSBwYWNrYWdlIHRoYXQgd2UnbGwgYmUgdXNpbmcgZXh0ZW5zaXZlbHkgaXMge3RpZHl2ZXJzZX0uIFRoZSB7dGlkeXZlcnNlfSBwYWNrYWdlIGlzIGFjdHVhbGx5IGEgW2NvbGxlY3Rpb24gb2YgUiBwYWNrYWdlc10oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy9wYWNrYWdlcykgZGVzaWduZWQgZm9yIHJlYWRpbmcsIHdyYW5nbGluZywgYW5kIGV4cGxvcmluZyBkYXRhIGFuZCB3aGljaCBhbGwgc2hhcmUgYW4gdW5kZXJseWluZyBkZXNpZ24gcGhpbG9zb3BoeSwgZ3JhbW1hciwgYW5kIGRhdGEgc3RydWN0dXJlcy4gVGhpcyBzaGFyZWQgZmVhdHVyZXMgYXJlIHNvbWV0aW1lcyAidGlkeSBkYXRhIHByaW5jaXBsZXMuIgoKQ2xpY2sgdGhlIGdyZWVuIGFycm93IGluIHRoZSByaWdodCBjb3JuZXIgb2YgdGhlICJjb2RlIGNodW5rIiB0aGF0IGZvbGxvd3MgdG8gbG9hZCB0aGUge3RpZHl2ZXJzZX0gbGlicmFyeS4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpBZ2FpbiwgZG9uJ3Qgd29ycnkgaWYgeW91IHNhdyBhIG51bWJlciBvZiBtZXNzYWdlczogdGhvc2UgcHJvYmFibHkgbWVhbiB0aGF0IHRoZSB0aWR5dmVyc2UgbG9hZGVkIGp1c3QgZmluZS4gQW55IGNvbmZsaWN0cyB5b3UgbWF5IGhhdmUgc2VlbiBtZWFuIHRoYXQgZnVuY3Rpb25zIGluIHRoZXNlIHBhY2thZ2VzIHlvdSBsb2FkZWQgaGF2ZSB0aGUgc2FtZSBuYW1lIGFzIGZ1bmN0aW9ucyBpbiBvdGhlciBwYWNrYWdlcyBhbmQgUiB3aWxsIGRlZmF1bHQgdG8gZnVuY3Rpb24gZnJvbSB0aGUgbGFzdCBsb2FkZWQgcGFja2FnZSB1bmxlc3MgeW91IHNwZWNpZnkgb3RoZXJ3aXNlLgoKIyMjIyB0aWR5dGV4dCDwn5OmCgohW10oaW1nL3RpZHl0ZXh0LnBuZyl7d2lkdGg9IjIwJSJ9CgpBcyB3ZSdsbCBsZWFybiBmaXJzdCBoYW5kIGluIHRoaXMgbW9kdWxlLCB1c2luZyB0aWR5IGRhdGEgcHJpbmNpcGxlcyBjYW4gYWxzbyBtYWtlIG1hbnkgdGV4dCBtaW5pbmcgdGFza3MgZWFzaWVyLCBtb3JlIGVmZmVjdGl2ZSwgYW5kIGNvbnNpc3RlbnQgd2l0aCB0b29scyBhbHJlYWR5IGluIHdpZGUgdXNlLiBUaGUge3RpZHl0ZXh0fSBwYWNrYWdlIGhlbHBzIHRvIGNvbnZlcnQgdGV4dCBpbnRvIGRhdGEgZnJhbWVzIG9mIGluZGl2aWR1YWwgd29yZHMsIG1ha2luZyBpdCBlYXN5IHRvIHRvIG1hbmlwdWxhdGUsIHN1bW1hcml6ZSwgYW5kIHZpc3VhbGl6ZSB0ZXh0IHVzaW5nIHVzaW5nIGZhbWlsaWFyIGZ1bmN0aW9ucyBmb3JtIHRoZSB7dGlkeXZlcnNlfSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzLgoKTGV0J3MgZ28gYWhlYWQgYW5kIGxvYWQgdGhlIHt0aWR5dGV4dH0gcGFja2FnZToKCmBgYHtyfQpsaWJyYXJ5KHRpZHl0ZXh0KQpgYGAKCkZvciBhIG1vcmUgY29tcHJlaGVuc2l2ZSBpbnRyb2R1Y3Rpb24gdG8gdGhlIGB0aWR5dGV4dGAgcGFja2FnZSwgd2UgY2Fubm90IHJlY29tbWVuZCBlbm91Z2ggdGhlIGZyZWUgb25saW5lIGJvb2ssICpUZXh0IE1pbmluZyB3aXRoIFI6IEEgVGlkeSBBcHByb2FjaCogW0BzaWxnZTIwMTd0ZXh0XS4gSWYgeW91J3JlIGludGVyZXN0ZWQgaW4gcHVyc3VpbmcgdGV4dCBhbmFseXNpcyB1c2luZyBSIHBvc3QgU3VtbWVyIFdvcmtzaG9wLCB0aGlzIHdpbGwgYmUgYSBnbyB0byByZWZlcmVuY2UuCgojIyAyLiBXUkFOR0xFCgpUaGUgaW1wb3J0YW5jZSBvZiBkYXRhIHdyYW5nbGluZywgcGFydGljdWxhcmx5IHdoZW4gd29ya2luZyB3aXRoIHRleHQsIGlzIGRpZmZpY3VsdCB0byBvdmVyc3RhdGUuIEp1c3QgYXMgYSByZWZyZXNoZXIsIHdyYW5nbGluZyBpbnZvbHZlcyB0aGUgaW5pdGlhbCBzdGVwcyBvZiBnb2luZyBmcm9tIHJhdyBkYXRhIHRvIGEgZGF0YXNldCB0aGF0IGNhbiBiZSBleHBsb3JlZCBhbmQgbW9kZWxlZCBbQGtydW1tMjAxOF0uIExlYXJuaW5nIExhYiAyIHdpbGwgaGF2ZSBhIGhlYXZ5IGVtcGhhc2lzIG9uIHByZXBhcmluZyB0ZXh0IGZvciBhbmFseXNpcyBhbmQgaW4gcGFydGljdWxhciB3ZSdsbCBsZWFybiBob3cgdG86CgphLiAgKipJbXBvcnQgRGF0YSoqLiBGaXJzdCB3ZSByZXZpc2l0IHRoZSBmYW1pbGlhciBgcmVhZF9jc3YoKWAgZnVuY3Rpb24gZm9yIHJlYWRpbmcgaW4gb3VyIENDU1MgYW5kIE5HU1MgdHdlZXRzCmIuICAqKlJlc3RydWN0dXJlIERhdGEqKi4gV2UgZm9jdXMgb24gcmVtb3ZpbmcgZXh0cmFuZW91cyBkYXRhIHVzaW5nIHRoZSBgc2VsZWN0KClgIGFuZCBgZmlsdGVyKClgIGZ1bmN0aW9ucyBmcm9tIHtkcGx5cn0sIGFuZCBpbnRyb2R1Y2UgdHdvIG5ldyBmdW5jdGlvbnMgZm9yIG1lcmdpbmcgdGhlIGRhdGEgZnJhbWVzIHRoYXQgd2UgaW1wb3J0ZWQuCmMuICAqKlRpZHkgVGV4dC4qKiBXZSBpbnRyb2R1Y2UgdGhlIHt0aWR5dGV4dH0gcGFja2FnZSB0byAidGlkeSIgYW5kIHRva2VuaXplIG91ciB0d2VldHMgaW4gb3JkZXIgdG8gY3JlYXRlIG91ciBkYXRhIGZyYW1lIGZvciBhbmFseXNpcyByZXZpc2l0IHRoZSBjb25jZXB0IG9mIGpvaW5zIHRvIHJlbW92ZSAic3RvcCB3b3JkcyIgdGhhdCBkb24ndCBhZGQgbXVjaCB2YWx1ZSB0byBvdXIgYW5hbHlzaXMuCgojIyMgMmEuIEltcG9ydCBhbmQgVmlldyBEYXRhCgpgYGB7cn0KY2Nzc190d2VldHMgPC0gcmVhZF9jc3YoImRhdGEvY2Nzcy10d2VldHMuY3N2IiwgCiAgICAgICAgICBjb2xfdHlwZXMgPSBjb2xzKGF1dGhvcl9pZCA9IGNvbF9jaGFyYWN0ZXIoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gY29sX2NoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJzYXRpb25faWQgPSBjb2xfY2hhcmFjdGVyKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBpbl9yZXBseV90b191c2VyX2lkID0gY29sX2NoYXJhY3RlcigpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICkKYGBgCgpOb3RlIHRoZSBhZGRpdGlvbiBvZiB0aGUgYGNvbF90eXBlcyA9YCBhcmd1bWVudCBmb3IgY2hhbmdpbmcgc29tZSBvZiB0aGUgY29sdW1uIHR5cGVzIHRvIGNoYXJhY3RlciBzdHJpbmdzIGJlY2F1c2UgdGhlIG51bWJlcnMgZm9yIHRob3NlIHBhcnRpY3VsYXIgY29sdW1ucyBhY3R1YWxseSBpbmRpY2F0ZSBpZGVudGlmaWVycyBmb3IgYXV0aG9ycyBhbmQgdHdlZXRzOgoKLSAgIGBhdXRob3JfaWRgID0gdGhlIGF1dGhvciBvZiB0aGUgdHdlZXQKCi0gICBgaWRgID0gdGhlIHVuaXF1ZSBpZCBmb3IgZWFjaCB0d2VldAoKLSAgIGBjb252ZXJhc3Rpb25faWRgID0gdGhlIHVuaXF1ZSBpZCBmb3IgZWFjaCBbY29udmVyc2F0aW9uIHRocmVhZF0oaHR0cHM6Ly9kZXZlbG9wZXIudHdpdHRlci5jb20vZW4vZG9jcy90d2l0dGVyLWFwaS9jb252ZXJzYXRpb24taWQpCgotICAgYGluX3JlcGx5X3RvX3VzZXJfaWRgID0gdGhlIGF1dGhvciBvZiB0aGUgdHdlZXQgYmVpbmcgcmVwbGllZCB0bwoKIyMjIyBbWW91ciBUdXJuXXtzdHlsZT0iY29sb3I6IGdyZWVuOyJ9wqDipLUge3N0eWxlPSJmb250LXN0eWxlOiBub3JtYWw7IGZvbnQtdmFyaWFudC1jYXBzOiBub3JtYWw7IGxldHRlci1zcGFjaW5nOiBub3JtYWw7IG9ycGhhbnM6IGF1dG87IHRleHQtYWxpZ246IHN0YXJ0OyB0ZXh0LWluZGVudDogMHB4OyB0ZXh0LXRyYW5zZm9ybTogbm9uZTsgd2hpdGUtc3BhY2U6IG5vcm1hbDsgd2lkb3dzOiBhdXRvOyB3b3JkLXNwYWNpbmc6IDBweDsgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiByZ2JhKDI2LCAyNiwgMjYsIDAuMyk7IC13ZWJraXQtdGV4dC1zaXplLWFkanVzdDogYXV0bzsgLXdlYmtpdC10ZXh0LXN0cm9rZS13aWR0aDogMHB4OyB0ZXh0LWRlY29yYXRpb246IG5vbmU7IGNhcmV0LWNvbG9yOiByZ2IoMCwgMCwgMCk7IGNvbG9yOiByZ2IoMCwgMCwgMCk7In0KCioqUlN0dWRpbyBUaXA6KiogSW1wb3J0aW5nIGRhdGEgYW5kIGRlYWxpbmcgd2l0aCBkYXRhIHR5cGVzIGNhbiBiZSBhIGJpdCB0cmlja3ksIGVzcGVjaWFsbHkgZm9yIGJlZ2lubmVycy4gRm9ydHVuYXRlbHksIFJTdHVkaW8gaGFzIGFuICJJbXBvcnQgRGF0YXNldCIgZmVhdHVyZSBpbiB0aGUgRW52aXJvbm1lbnQgUGFuZSB0aGF0IGNhbiBoZWxwIHlvdSB1c2UgdGhlIHtyZWFkcn0gcGFja2FnZSBhbmQgYXNzb2NpYXRlZCBmdW5jdGlvbnMgdG8gZ3JlYXRseSBmYWNpbGl0YXRlIHRoaXMgcHJvY2Vzcy4KCiFbXShpbWcvaW1wb3J0LWRhdGEucG5nKQoKVHJ5IHVzaW5nIHRoZSAiSW1wb3J0IERhdGFzZXQiIGZlYXR1cmUgaW4gdGhlIHVwcGVyIHJpZ2h0IGVudmlyb25tZW50IHBhbmUgdG8gaW1wb3J0IHRoZSBOR1NTIHR3ZWV0cyBsb2NhdGVkIGluIHRoZSBkYXRhIGZvbGRlci4KClRoZSBjb2RlIGdlbmVyYXRlZCBzaG91bGQgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOgoKYGBge3J9Cm5nc3NfdHdlZXRzIDwtIHJlYWRfY3N2KCJkYXRhL25nc3MtdHdlZXRzLmNzdiIsIAogICAgICAgICAgY29sX3R5cGVzID0gY29scyhhdXRob3JfaWQgPSBjb2xfY2hhcmFjdGVyKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBpZCA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVyc2F0aW9uX2lkID0gY29sX2NoYXJhY3RlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5fcmVwbHlfdG9fdXNlcl9pZCA9IGNvbF9jaGFyYWN0ZXIoKQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICApCmBgYAoKVXNlIHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuayB0byBpbnNwZWN0IHlvdXIgdHdlZXRzIHVzaW5nIGEgZnVuY3Rpb24geW91J3ZlIGxlYXJuZWQgc28gZm9yIGZvciB2aWV3aW5nIHlvdXIgZGF0YToKCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCmBgYAoKIyMjIDJiLiBSZXN0cnVjdHVyZSBEYXRhCgojIyMjIFN1YnNldCBUd2VldHMKCkFzIHlvdSBtYXkgaGF2ZSBub3RpY2VkLCB3ZSBoYXZlIG1vcmUgZGF0YSB0aGFuIHdlIG5lZWQgZm9yIG91ciBhbmFseXNpcyBhbmQgc2hvdWxkIHByb2JhYmx5IHBhcmUgaXQgZG93biB0byBqdXN0IHdoYXQgd2UnbGwgdXNlLgoKTGV0J3Mgc3RhcnQgd2l0aCB0aGUgRmlyc3QsIHNpbmNlIHRoaXMgaXMgYSBmYW1pbHkgZnJpZW5kbHkgbGVhcm5pbmcgbGFiLCBsZXQncyB1c2UgdGhlIGBmaWx0ZXIoKWAgZnVuY3Rpb24gaW50cm9kdWNlZCBpbiBwcmV2aW91cyBsYWJzIHRvIGZpbHRlciBvdXQgcm93cyBjb250YWluaW5nICJwb3NzaWJseSBzZW5zaXRpdmUiIGxhbmd1YWdlOgoKYGBge3IsIGV2YWw9VFJVRX0KY2Nzc190d2VldHNfMSA8LSBjY3NzX3R3ZWV0cyAlPiUgCiAgZmlsdGVyKHBvc3NpYmx5X3NlbnNpdGl2ZSA9PSAiRkFMU0UiKQpgYGAKCk5vdyBsZXQncyB1c2UgdGhlIGBzZWxlY3QoKWAgZnVuY3Rpb24gdG8gc2VsZWN0IHRoZSBmb2xsb3dpbmcgY29sdW1ucyBmcm9tIG91ciBuZXcgYHNzX3R3ZWV0c19jbGVhbmAgZGF0YSBmcmFtZToKCjEuICBgdGV4dGAgY29udGFpbmluZyB0aGUgdHdlZXQgd2hpY2ggaXMgb3VyIHByaW1hcnkgZGF0YSBzb3VyY2Ugb2YgaW50ZXJlc3QKMi4gIGBhdXRob3JfaWRgIG9mIHRoZSB1c2VyIHdobyBjcmVhdGVkIHRoZSB0d2VldAozLiAgYGNyZWF0ZWRfYXRgIHRpbWVzdGFtcCBmb3IgZXhhbWluaW5nIGNoYW5nZXMgaW4gc2VudGltZW50IG92ZXIgdGltZQo0LiAgYGNvbnZlcnNhdGlvbl9pZGAgZm9yIGV4YW1pbmluZyBzZW50aW1lbnQgYnkgY29udmVyc2F0aW9ucwo1LiAgYGlkYCBmb3IgdGhlIHVuaXF1ZSByZWZlcmVuY2UgaWQgZm9yIGVhY2ggdHdlZXQgYW5kIHVzZWZ1bCBmb3IgY291bnRzCgpgYGB7ciBzZWxlY3QtdmFyaWFibGVzLCBldmFsPVRSVUV9CmNjc3NfdHdlZXRzXzIgPC0gY2Nzc190d2VldHNfMSAlPiUgCiAgc2VsZWN0KHRleHQsCiAgICAgICAgIGF1dGhvcl9pZCwKICAgICAgICAgY3JlYXRlZF9hdCwgCiAgICAgICAgIGNvbnZlcnNhdGlvbl9pZCwKICAgICAgICAgaWQpCmBgYAoKIyMjIyBbWW91ciBUdXJuXXtzdHlsZT0iY29sb3I6IGdyZWVuOyJ9wqDipLUge3N0eWxlPSJmb250LXN0eWxlOiBub3JtYWw7IGZvbnQtdmFyaWFudC1jYXBzOiBub3JtYWw7IGxldHRlci1zcGFjaW5nOiBub3JtYWw7IG9ycGhhbnM6IGF1dG87IHRleHQtYWxpZ246IHN0YXJ0OyB0ZXh0LWluZGVudDogMHB4OyB0ZXh0LXRyYW5zZm9ybTogbm9uZTsgd2hpdGUtc3BhY2U6IG5vcm1hbDsgd2lkb3dzOiBhdXRvOyB3b3JkLXNwYWNpbmc6IDBweDsgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiByZ2JhKDI2LCAyNiwgMjYsIDAuMyk7IC13ZWJraXQtdGV4dC1zaXplLWFkanVzdDogYXV0bzsgLXdlYmtpdC10ZXh0LXN0cm9rZS13aWR0aDogMHB4OyB0ZXh0LWRlY29yYXRpb246IG5vbmU7IGNhcmV0LWNvbG9yOiByZ2IoMCwgMCwgMCk7IGNvbG9yOiByZ2IoMCwgMCwgMCk7In0KCioqTm90ZToqKiBUaGUgYHNlbGVjdCgpYCBmdW5jdGlvbiB3aWxsIGFsc28gcmVvcmRlciB5b3VyIGNvbHVtbnMgYmFzZWQgb24gdGhlIG9yZGVyIGluIHdoaWNoIHlvdSBsaXN0IHRoZW0uCgpVc2UgdGhlIGNvZGUgY2h1bmsgYmVsb3cgdG8gcmVvcmRlciB0aGUgY29sdW1ucyB0byB5b3VyIGxpa2luZyBhbmQgYXNzaWduIHRvIGBjY3NzX3R3ZWV0c18zYDoKCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCmBgYAoKIyMjIyBBZGQgJiBSZWxvY2F0ZSBDb2x1bW5zCgpGaW5hbGx5LCBzaW5jZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBjb21wYXJpbmcgdGhlIHNlbnRpbWVudCBvZiBOR1NTIHR3ZWV0cyB3aXRoIENTU1MgdHdlZXRzLCBpdCB3b3VsZCBiZSBoZWxwZnVsIGlmIHdlIGhhZCBhIGNvbHVtbiB0byBxdWlja2x5IGlkZW50aWZ5IHRoZSBzZXQgb2Ygc3RhdGUgc3RhbmRhcmRzIHdpdGggd2hpY2ggZWFjaCB0d2VldCBpcyBhc3NvY2lhdGVkLgoKV2UnbGwgdXNlIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYHN0YW5kYXJkc2AgdG8gbGFiZWwgZWFjaCB0d2VldHMgYXMgIm5nc3MiOgoKYGBge3J9CmNjc3NfdHdlZXRzXzQgPC0gbXV0YXRlKGNjc3NfdHdlZXRzXzIsIHN0YW5kYXJkcyA9ICJjY3NzIikKCmNvbG5hbWVzKGNjc3NfdHdlZXRzXzQpCmBgYAoKQW5kIGp1c3QgYmVjYXVzZSBpdCBib3RoZXJzIG1lLCBJJ20gZ29pbmcgdG8gdXNlIHRoZSBgcmVsb2NhdGUoKWAgZnVuY3Rpb24gdG8gbW92ZSB0aGUgYHN0YW5kYXJkc2AgY29sdW1uIHRvIHRoZSBmaXJzdCBwb3NpdGlvbiBzbyBJIGNhbiBxdWlja2x5IHNlZSB3aGljaCBzdGFuZGFyZHMgdGhlIHR3ZWV0IGlzIGZyb206CgpgYGB7cn0KY2Nzc190d2VldHNfNSA8LSByZWxvY2F0ZShjY3NzX3R3ZWV0c180LCBzdGFuZGFyZHMpCgpjb2xuYW1lcyhjY3NzX3R3ZWV0c181KQpgYGAKCkFnYWluLCB3ZSBjb3VsZCBhbHNvIGhhdmUgdXNlZCB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbiB0byByZW9yZGVyIGNvbHVtbnMgbGlrZSBzbzoKCmBgYHtyfQpjY3NzX3R3ZWV0c181IDwtIGNjc3NfdHdlZXRzXzQgJT4lIAogIHNlbGVjdChzdGFuZGFyZHMsCiAgICAgICAgIHRleHQsCiAgICAgICAgIGF1dGhvcl9pZCwKICAgICAgICAgY3JlYXRlZF9hdCwgCiAgICAgICAgIGNvbnZlcnNhdGlvbl9pZCwKICAgICAgICAgaWQpCgpjb2xuYW1lcyhjY3NzX3R3ZWV0c181KQpgYGAKCkJlZm9yZSBtb3Zpbmcgb24gdG8gdGhlIENDU1Mgc3RhbmRhcmRzLCBsZXQncyB1c2UgdGhlIGAlPiVgIG9wZXJhdG9yIGFuZCByZXdyaXRlIHRoZSBjb2RlIGZyb20gb3VyIHdyYW5nbGluZyBzbyB0aGVyZSBpcyBsZXNzIHJlZHVuZGFuY3kgYW5kIGl0IGlzIGVhc2llciB0byByZWFkOgoKYGBge3J9CiMgU2VhcmNoIFR3ZWV0cwpjY3NzX3R3ZWV0c19jbGVhbiA8LSBjY3NzX3R3ZWV0cyAlPiUKICBmaWx0ZXIocG9zc2libHlfc2Vuc2l0aXZlID09ICJGQUxTRSIpICU+JQogIHNlbGVjdCh0ZXh0LCBhdXRob3JfaWQsIGNyZWF0ZWRfYXQsIGNvbnZlcnNhdGlvbl9pZCwgaWQpICU+JQogIG11dGF0ZShzdGFuZGFyZHMgPSAiY2NzcyIpICU+JQogIHJlbG9jYXRlKHN0YW5kYXJkcykKCmhlYWQoY2Nzc190d2VldHNfY2xlYW4pCmBgYAoKIyMjIyBbWW91ciBUdXJuXXtzdHlsZT0iY29sb3I6IGdyZWVuOyJ9IOKktQoKUmVjYWxsIGZyb20gc2VjdGlvbiBbMWIuIERlZmluZSBRdWVzdGlvbnNdIHRoYXQgd2UgYXJlIGludGVyZXN0ZWQgaW4gY29tcGFyaW5nIHdvcmQgdXNhZ2UgYW5kIHB1YmxpYyBzZW50aW1lbnQgYXJvdW5kIGJvdGggdGhlIENvbW1vbiBDb3JlIGFuZCBOZXh0IEdlbiBTY2llbmNlIFN0YW5kYXJkcy4KCkNyZWF0ZSBhbiBuZXcgYG5nc3NfdHdlZXRzX2NsZWFuYCBkYXRhIGZyYW1lIGNvbnNpc3Rpbmcgb2YgdGhlIE5leHQgR2VuZXJhdGlvbiBTY2llbmNlIFN0YW5kYXJkcyB0d2VldHMgd2UgaW1wb3J0ZWQgYnkgdXNlIHRoZSBjb2RlIGFib3ZlIGFzIGEgZ3VpZGUuCgpgYGB7cn0KIyB5b3VyIGNvZGUgaGVyZQpgYGAKClRyeSBub3QgdG8gcGVlayBhdCB0aGUgYW5zd2VyIGJlbG93IHVubGVzcyB5b3UgYXJlIGhhdmluZyBkaWZmaWN1bHR5IHdpdGggeW91ciBjb2RlLgoKIyMjIyBBbnN3ZXIKCmBgYHtyIDJiLWFuc3dlcn0Kbmdzc190d2VldHNfY2xlYW4gPC0gbmdzc190d2VldHMgJT4lCiAgZmlsdGVyKHBvc3NpYmx5X3NlbnNpdGl2ZSA9PSAiRkFMU0UiKSAlPiUKICBzZWxlY3QodGV4dCwgYXV0aG9yX2lkLCBjcmVhdGVkX2F0LCBjb252ZXJzYXRpb25faWQsIGlkKSAlPiUKICBtdXRhdGUoc3RhbmRhcmRzID0gIm5nc3MiKSAlPiUKICByZWxvY2F0ZShzdGFuZGFyZHMpCgpoZWFkKG5nc3NfdHdlZXRzX2NsZWFuKQpgYGAKCiMjIyMgTWVyZ2UgRGF0YSBGcmFtZXMKCkZpbmFsbHksIGxldCdzIGNvbWJpbmUgb3VyIENDU1MgYW5kIE5HU1MgdHdlZXRzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBieSB1c2luZyB0aGUgYHVuaW9uKClgIGZ1bmN0aW9uIGZyb20gYGRwbHlyYCBhbmQgc2ltcGx5IHN1cHBseWluZyB0aGUgZGF0YSBmcmFtZXMgdGhhdCB5b3Ugd2FudCB0byBjb21iaW5lIGFzIGFyZ3VtZW50czoKCmBgYHtyfQpzc190d2VldHMgPC0gdW5pb24oY2Nzc190d2VldHNfY2xlYW4sCiAgICAgICAgICAgICAgICAgICBuZ3NzX3R3ZWV0c19jbGVhbikKYGBgCgpOb3RlIHRoYXQgd2hlbiBjcmVhdGluZyBhICJ1bmlvbiIgbGlrZSB0aGlzIChpLmUuIHN0YWNraW5nIG9uZSBkYXRhIGZyYW1lIG9uIHRvcCBvZiBhbm90aGVyKSwgeW91IHNob3VsZCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBjb2x1bW5zIGluIGVhY2ggZGF0YSBmcmFtZSBhbmQgdGhleSBzaG91bGQgYmUgaW4gdGhlIGV4YWN0IHNhbWUgb3JkZXIuCgojIyMjIFtZb3VyIFR1cm5de3N0eWxlPSJjb2xvcjogZ3JlZW47In0g4qS1CgpGaW5hbGx5LCBsZXQncyB0YWtlIGEgcXVpY2sgbG9vayBhdCBib3RoIHRoZSBgaGVhZCgpYCBhbmQgdGhlIGB0YWlsKClgIG9mIHRoaXMgbmV3IGBzc190d2VldHNgIGRhdGEgZnJhbWUgdG8gbWFrZSBzdXJlIGl0IGNvbnRhaW5zIGJvdGggIm5nc3MiIGFuZCAiY2NzcyIgc3RhbmRhcmRzOgoKYGBge3J9CmhlYWQoc3NfdHdlZXRzKQp0YWlsKHNzX3R3ZWV0cykKYGBgCgpXb3csIHNvIG11Y2ggZm9yIGEgZmFtaWx5IGZyaWVuZGx5IGxlYXJuaW5nIGxhYiEgQmFzZWQgb24gdGhpcyB2ZXJ5IGxpbWl0ZWQgc2FtcGxlLCB3aGljaCBzZXQgb2Ygc3RhbmRhcmRzIGRvIHlvdSB0aGluayBUd2l0dGVyIHVzZXJzIGFyZSBtb3JlIG5lZ2F0aXZlIGFib3V0PwoKLSAgIHlvdXIgcmVzcG9uc2UgaGVyZQoKTGV0J3MgdGFrZSBhIHNsaWdodGx5IGxhcmdlciBzYW1wbGUgb2YgdGhlIENDU1MgdHdlZXRzOgoKYGBge3J9CnNzX3R3ZWV0cyAlPiUgCiAgZmlsdGVyKHN0YW5kYXJkcyA9PSAiY2NzcyIpICU+JQogIHNhbXBsZV9uKDIwKSAlPiUKICByZWxvY2F0ZSh0ZXh0KQogICAgICAgICAgIApgYGAKCiMjIyMgW1lvdXIgVHVybl17c3R5bGU9ImNvbG9yOiBncmVlbjsifSDipLUKClVzZSB0aGUgY29kZSBjaHVuayBiZWxvdyB0byB0YWtlIGEgc2FtcGxlIG9mIHRoZSBOR1NTIHR3ZWV0czoKCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCmBgYAoKU3RpbGwgb2YgdGhlIHNhbWUgb3Bpbmlvbj8KCi0gICBSZXNwb25kIGhlcmUuLi4KCiMjIyAyYy4gVGlkeSBUZXh0CgpUZXh0IGRhdGEgYnkgaXQncyB2ZXJ5IG5hdHVyZSBpcyBFU1BFQ0lBTExZIHVudGlkeSBhbmQgaXMgc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzICJ1bnN0cnVjdHVyZWQiIGRhdGEuIEluIHRoaXMgc2VjdGlvbiB3ZSBhcmUgaW50cm9kdWNlZCB0byB0aGUgYHRpZHl0ZXh0YCBwYWNrYWdlIGFuZCB3aWxsIGxlYXJuIHNvbWUgbmV3IGZ1bmN0aW9ucyB0byBjb252ZXJ0IHRleHQgdG8gYW5kIGZyb20gdGlkeSBmb3JtYXRzLiBIYXZpbmcgb3VyIHRleHQgaW4gYSB0aWR5IGZvcm1hdCB3aWxsIGFsbG93IHVzIHRvIHN3aXRjaCBzZWFtbGVzc2x5IGJldHdlZW4gdGlkeSB0b29scyBhbmQgZXhpc3RpbmcgdGV4dCBtaW5pbmcgcGFja2FnZXMsIHdoaWxlIGFsc28gbWFraW5nIGl0IGVhc2llciB0byB2aXN1YWxpemUgdGV4dCBzdW1tYXJpZXMgaW4gb3RoZXIgZGF0YSBhbmFseXNpcyB0b29scyBsaWtlIFRhYmxlYXUuCgojIyMjIFRva2VuaXplIFRleHQge2RhdGEtbGluaz0iMmIuIFRpZHkgVGV4dCJ9CgpJbiBDaGFwdGVyIDEgb2YgVGV4dCBNaW5pbmcgd2l0aCBSLCBAc2lsZ2UyMDE3dGV4dCBkZWZpbmUgdGhlIHRpZHkgdGV4dCBmb3JtYXQgYXMgYSB0YWJsZSB3aXRoIG9uZS10b2tlbi1wZXItcm93LCBhbmQgZXhwbGFpbiB0aGF0OgoKPiBBICoqdG9rZW4qKiBpcyBhIG1lYW5pbmdmdWwgdW5pdCBvZiB0ZXh0LCBzdWNoIGFzIGEgd29yZCwgdHdvLXdvcmQgcGhyYXNlIChiaWdyYW0pLCBvciBzZW50ZW5jZSB0aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluIHVzaW5nIGZvciBhbmFseXNpcy4gQW5kIHRva2VuaXphdGlvbiBpcyB0aGUgcHJvY2VzcyBvZiBzcGxpdHRpbmcgdGV4dCBpbnRvIHRva2Vucy4KClRoaXMgb25lLXRva2VuLXBlci1yb3cgc3RydWN0dXJlIGlzIGluIGNvbnRyYXN0IHRvIHRoZSB3YXlzIHRleHQgaXMgb2Z0ZW4gc3RvcmVkIGZvciB0ZXh0IGFuYWx5c2lzLCBwZXJoYXBzIGFzIHN0cmluZ3MgaW4gYSBjb3JwdXMgb2JqZWN0IG9yIGluIGEgZG9jdW1lbnQtdGVybSBtYXRyaXguIEZvciB0aWR5IHRleHQgbWluaW5nLCB0aGXCoHRva2VuIHRoYXQgaXMgc3RvcmVkIGluIGVhY2ggcm93IGlzIG1vc3Qgb2Z0ZW4gYSBzaW5nbGUgd29yZCwgYnV0IGNhbiBhbHNvIGJlIGFuIG4tZ3JhbSwgc2VudGVuY2UsIG9yIHBhcmFncmFwaC4KCkZvciB0aGlzIHBhcnQgb2Ygb3VyIHdvcmtmbG93LCBvdXIgZ29hbCBpcyB0byB0cmFuc2Zvcm0gb3VyIGBzc190d2VldHNgIGRhdGEgZnJvbSB0aGlzOgoKYGBge3J9CmhlYWQocmVsb2NhdGUoc3NfdHdlZXRzLCB0ZXh0KSkKYGBgCgpJbnRvIGEgInRpZHkgdGV4dCIgb25lLXRva2VuLXBlci1yb3cgZm9ybWF0IHRoYXQgbG9va3MgbGlrZSB0aGlzOgoKYGBge3J9CnRpZHlfdHdlZXRzIDwtIHNzX3R3ZWV0cyAlPiUgCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCAKICAgICAgICAgICAgICAgIGlucHV0ID0gdGV4dCkgJT4lCiAgcmVsb2NhdGUod29yZCkKCmhlYWQodGlkeV90d2VldHMpCmBgYAoKTGF0ZXIgaW4gdGhlIHllYXIsIHdlJ2xsIGxlYXJuIGFib3V0IG90aGVyIGRhdGEgc3RydWN0dXJlcyBmb3IgdGV4dCBhbmFseXNpcyBsaWtlIHRoZSBkb2N1bWVudC10ZXJtIG1hdHJpeCBhbmQgY29ycHVzIG9iamVjdHMuIEZvciBub3csIGhvd2V2ZXIsIHdvcmtpbmcgd2l0aCB0aGUgZmFtaWxpYXIgdGlkeSBkYXRhIGZyYW1lIGFsbG93cyB1cyB0byB0YWtlIGFkdmFudGFnZSBvZiBwb3B1bGFyIHBhY2thZ2VzIHRoYXQgdXNlIHRoZSBzaGFyZWQgdGlkeXZlcnNlIHN5bnRheCBhbmQgcHJpbmNpcGxlcyBmb3Igd3JhbmdsaW5nLCBleHBsb3JpbmcsIGFuZCBtb2RlbGluZyBkYXRhLgoKIyMjIyBVbmlncmFtcwoKQXMgZGVtb25zdHJhdGVkIGFib3ZlLCB0aGUgYHRpZHl0ZXh0YCBwYWNrYWdlIHByb3ZpZGVzIHRoZSBpbmNyZWRpYmx5IHBvd2VyZnVsIGB1bm5lc3RfdG9rZW5zKClgIGZ1bmN0aW9uIHRvIHRva2VuaXplIHRleHQgKGluY2x1ZGluZyB0d2VldHMhKSBhbmQgY29udmVydCB0aGVtIHRvIGEgb25lLXRva2VuLXBlci1yb3cgZm9ybWF0LgoKTGV0J3MgdG9rZW5pemUgb3VyIHR3ZWV0cyBieSB1c2luZyB0aGlzIGZ1bmN0aW9uIHRvIHNwbGl0IGVhY2ggdHdlZXQgaW50byBhIHNpbmdsZSByb3cgdG8gbWFrZSBpdCBlYXNpZXIgdG8gYW5hbHl6ZSBhbmQgdGFrZSBhIGxvb2s6CgpgYGB7ciB1bm5lc3QtdG9rZW5zfQpzc190b2tlbnMgPC0gdW5uZXN0X3Rva2Vucyhzc190d2VldHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dCA9IHdvcmQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0ID0gdGV4dCkKCmhlYWQocmVsb2NhdGUoc3NfdG9rZW5zLCB3b3JkKSkKYGBgCgpUaGVyZSBpcyBBIExPVCB0byB1bnBhY2sgd2l0aCB0aGlzIGZ1bmN0aW9uOgoKLSAgIEZpcnN0IG5vdGljZSB0aGF0wqBgdW5uZXN0X3Rva2VucygpYCBleHBlY3RzIGEgZGF0YSBmcmFtZSBhcyB0aGUgZmlyc3QgYXJndW1lbnQsIGZvbGxvd2VkIGJ5IHR3byBjb2x1bW4gbmFtZXMuCi0gICBUaGUgbmV4dCBhcmd1bWVudCBpcyBhbiBvdXRwdXQgY29sdW1uIG5hbWUgdGhhdCBkb2Vzbid0IGN1cnJlbnRseSBleGlzdCBidXQgd2lsbCBiZSBjcmVhdGVkIGFzIHRoZSB0ZXh0IGlzICJ1bm5lc3RlZCIgaW50byBpdCwgYHdvcmRgIGluIHRoaXMgY2FzZSkuCi0gICBUaGlzIGlzIGZvbGxvd2VkIGJ5IHRoZSBpbnB1dCBjb2x1bW4gdGhhdCB0aGUgdGV4dCBjb21lcyBmcm9tLCB3aGljaCB3ZSB1bmNyZWF0aXZlbHkgbmFtZWQgYHRleHRgLgotICAgQnkgZGVmYXVsdCwgYSB0b2tlbiBpcyBhbiBpbmRpdmlkdWFsIHdvcmQgb3IgdW5pZ3JhbS4KLSAgIE90aGVyIGNvbHVtbnMsIHN1Y2ggYXPCoGBhdXRob3JfaWRgwqBhbmTCoGBjcmVhdGVkX2F0YCwgYXJlIHJldGFpbmVkLgotICAgQWxsIHB1bmN0dWF0aW9uIGhhcyBiZWVuIHJlbW92ZWQuCi0gICBUb2tlbnMgaGF2ZSBiZWVuIGNoYW5nZWQgdG8gbG93ZXJjYXNlLCB3aGljaCBtYWtlcyB0aGVtIGVhc2llciB0byBjb21wYXJlIG9yIGNvbWJpbmUgd2l0aCBvdGhlciBkYXRhc2V0cyAodXNlIHRoZcKgYHRvX2xvd2VyID0gRkFMU0VgwqBhcmd1bWVudCB0byB0dXJuIG9mZiBpZiBkZXNpcmVkKS4KCioqTm90ZToqKiBTaW5jZSB7dGlkeXRleHR9IGZvbGxvd3MgdGlkeSBkYXRhIHByaW5jaXBsZXMsIHdlIGFsc28gY291bGQgaGF2ZSB1c2VkIHRoZSBgJT4lYCBvcGVyYXRvciB0byBwYXNzIG91ciBkYXRhIGZyYW1lIHRvIHRoZSBgdW5uZXN0X3Rva2VucygpYCBmdW5jdGlvbiBsaWtlIHNvOgoKYGBge3J9CnNzX3Rva2VucyA8LSBzc190d2VldHMgJT4lCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCAKICAgICAgICAgICAgICAgIGlucHV0ID0gdGV4dCkKYGBgCgojIyMjIFtZb3VyIFR1cm5de3N0eWxlPSJjb2xvcjogZ3JlZW47In3CoOKktSB7c3R5bGU9ImZvbnQtc3R5bGU6IG5vcm1hbDsgZm9udC12YXJpYW50LWNhcHM6IG5vcm1hbDsgbGV0dGVyLXNwYWNpbmc6IG5vcm1hbDsgb3JwaGFuczogYXV0bzsgdGV4dC1hbGlnbjogc3RhcnQ7IHRleHQtaW5kZW50OiAwcHg7IHRleHQtdHJhbnNmb3JtOiBub25lOyB3aGl0ZS1zcGFjZTogbm9ybWFsOyB3aWRvd3M6IGF1dG87IHdvcmQtc3BhY2luZzogMHB4OyAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHJnYmEoMjYsIDI2LCAyNiwgMC4zKTsgLXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OiBhdXRvOyAtd2Via2l0LXRleHQtc3Ryb2tlLXdpZHRoOiAwcHg7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgY2FyZXQtY29sb3I6IHJnYigwLCAwLCAwKTsgY29sb3I6IHJnYigwLCAwLCAwKTsifQoKVGhlIGB1bm5lc3RfdG9rZW5zKClgIGZ1bmN0aW9uIGFsc28gaGFzIGEgc3BlY2lhbGl6ZWTCoGDigJx0d2VldHPigJ1gwqB0b2tlbml6ZXIgaW4gdGhlIGB0b2tlbnMgPWAgYXJndW1lbnQgdGhhdCBpcyB2ZXJ5IHVzZWZ1bCBmb3IgZGVhbGluZyB3aXRoIFR3aXR0ZXIgdGV4dC4gSXQgcmV0YWlucyBoYXNodGFncyBhbmQgbWVudGlvbnMgb2YgdXNlcm5hbWVzIHdpdGggdGhlIFxAIHN5bWJvbCBhcyBpbGx1c3RyYXRlZCBieSBvdXIgXEBjYXR0dXJkMiBmcmllbmQgd2hvIGZlYXR1cmVkIHByb21pbmVudGx5IGluIG91ciB0aGUgZmlyc3QgQ0NTUyB0d2VldC4KClJld3JpdGUgdGhlIGNvZGUgYWJvdmUgKCoqeW91IGNhbiBjaGVjayBhbnN3ZXIgYmVsb3cqKikgdG8gaW5jbHVkZSB0aGUgdG9rZW4gYXJndW1lbnQgc2V0IHRvICJ0d2VldHMiLCBhc3NpZ24gdG8gYHNzX3Rva2Vuc18xYCwgYW5kIGFuc3dlciB0aGUgcXVlc3Rpb25zIHRoYXQgZm9sbG93OgoKYGBge3J9CiMgeW91ciBjb2RlIGhlcmUKYGBgCgoxLiAgSG93IG1hbnkgb2JzZXJ2YXRpb25zIHdlcmUgb3VyIG9yaWdpbmFsIGBzc190d2VldHNgIGRhdGEgZnJhbWU/CgogICAgLSAgIAoKMi4gIEhvdyBtYW55IG9ic2VydmF0aW9ucyBhcmUgdGhlcmUgbm93PyBXaHkgdGhlIGRpZmZlcmVuY2U/CgogICAgLSAgIAoKIyMjIyBBbnN3ZXIKCllvdXIgY29kZSBzaG91bGQgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOgoKYGBge3J9CnNzX3Rva2Vuc18xIDwtIHVubmVzdF90b2tlbnMoc3NfdHdlZXRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gd29yZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0ID0gdGV4dCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRva2VuID0gInR3ZWV0cyIpCgpoZWFkKHNzX3Rva2Vuc18xKQpgYGAKCkJlZm9yZSB3ZSBtb3ZlIGFueSBmdXJ0aGVyIGxldCdzIHRha2UgYSBxdWljayBsb29rIGF0IHRoZSBtb3N0IGNvbW1vbiB3b3JkIGluIG91ciB0d28gZGF0YXNldHM6CgpgYGB7cn0Kc3NfdG9rZW5zXzEgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCgpgYGAKCldlbGwsIG1hbnkgb2YgdGhlc2UgdHdlZXRzIGFyZSBjbGVhcmx5IGFib3V0IHRoZSBjY3NzIGFuZCBtYXRoIGF0IGxlYXN0LCBidXQgYmV5b25kIHRoYXQgaXQncyBhIGJpdCBoYXJkIHRvIHRlbGwgYmVjYXVzZSB0aGVyZSBhcmUgc28gbWFueSAic3RvcCB3b3JkcyIgbGlrZSAidGhlIiwgInRvIiwgImFuZCIsICJpbiIgdGhhdCBkb24ndCBjYXJyeSBtdWNoIG1lYW5pbmcgYnkgdGhlbXNlbHZlcy4KCiMjIyMgUmVtb3ZlIFN0b3AgV29yZHMKCk9mdGVuIGluIHRleHQgYW5hbHlzaXMsIHdlIHdpbGwgd2FudCB0byByZW1vdmUgdGhlc2Ugc3RvcCB3b3JkcyBpZiB0aGV5IGFyZSBub3QgdXNlZnVsIGZvciBhbiBhbmFseXNpcy4gVGhlwqBgc3RvcF93b3Jkc2DCoGRhdGFzZXQgaW4gdGhlIHt0aWR5dGV4dH0gcGFja2FnZSBjb250YWlucyBzdG9wIHdvcmRzIGZyb20gdGhyZWUgbGV4aWNvbnMuIFdlIGNhbiB1c2UgdGhlbSBhbGwgdG9nZXRoZXIsIGFzIHdlIGhhdmUgaGVyZSwgb3LCoGBmaWx0ZXIoKWDCoHRvIG9ubHkgdXNlIG9uZSBzZXQgb2Ygc3RvcCB3b3JkcyBpZiB0aGF0IGlzIG1vcmUgYXBwcm9wcmlhdGUgZm9yIGEgY2VydGFpbiBhbmFseXNpcy4KCkxldCdzIHRha2UgYSBjbG9zZXIgdGhlIGxleGljb25zIGFuZCBzdG9wIHdvcmRzIGluY2x1ZGVkIGluIGVhY2g6CgpgYGB7ciwgZXZhbD1GQUxTRX0KVmlldyhzdG9wX3dvcmRzKQpgYGAKCiMjIyMgVGhlIGBhbnRpX2pvaW5gIEZ1bmN0aW9uCgpJbiBvcmRlciB0byByZW1vdmUgdGhlc2Ugc3RvcCB3b3Jkcywgd2Ugd2lsbCB1c2UgYSBmdW5jdGlvbiBjYWxsZWTCoGBhbnRpX2pvaW4oKWDCoHRoYXQgbG9va3MgZm9yIG1hdGNoaW5nIHZhbHVlcyBpbiBhIHNwZWNpZmljIGNvbHVtbiBmcm9tIHR3byBkYXRhc2V0cyBhbmQgcmV0dXJucyByb3dzIGZyb20gdGhlIG9yaWdpbmFsIGRhdGFzZXQgdGhhdCBoYXZlIG5vIG1hdGNoZXMgbGlrZSBzbzoKCiFbXShpbWcvYW50aS1qb2luLnBuZykKCkZvciBhIGdvb2Qgb3ZlcnZpZXcgb2YgdGhlIGRpZmZlcmVudMKgYGRwbHlyYMKgam9pbnMgc2VlIGhlcmU6wqA8aHR0cHM6Ly9tZWRpdW0uY29tL3RoZS1jb2RlaHViL2JlZ2lubmVycy1ndWlkZS10by11c2luZy1qb2lucy1pbi1yLTY4MmZjOWIxZjExOT4uCgpOb3cgbGV0J3MgcmVtb3ZlIHN0b3Agd29yZHMgdGhhdCBkb24ndCBoZWxwIHVzIGxlYXJuIG11Y2ggYWJvdXQgd2hhdCBwZW9wbGUgYXJlIHNheWluZyBhYm91dCB0aGUgc3RhdGUgc3RhbmRhcmRzLgoKYGBge3Igc3RvcC11bmlncmFtc30Kc3NfdG9rZW5zXzIgPC0gYW50aV9qb2luKHNzX3Rva2Vuc18xLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wX3dvcmRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ3b3JkIikKCmhlYWQoc3NfdG9rZW5zXzIpCmBgYAoKTm90aWNlIHRoYXQgd2UndmUgc3BlY2lmaWVkIHRoZSBgYnkgPWAgYXJndW1lbnQgdG8gbG9vayBmb3IgbWF0Y2hpbmcgd29yZHMgaW4gdGhlIGB3b3JkYCBjb2x1bW4gZm9yIGJvdGggZGF0YSBzZXRzIGFuZCByZW1vdmUgYW55IHJvd3MgZnJvbSB0aGUgYHR3ZWV0X3Rva2Vuc2AgZGF0YXNldCB0aGF0IG1hdGNoIHRoZSBgc3RvcF93b3Jkc2AgZGF0YXNldC4gUmVtZW1iZXIgd2hlbiB3ZSBmaXJzdCB0b2tlbml6ZWQgb3VyIGRhdGFzZXQgSSBjb252ZW5pZW50bHkgY2hvc2XCoGBvdXRwdXQgPSB3b3JkYMKgYXMgdGhlIGNvbHVtbiBuYW1lIGJlY2F1c2UgaXQgbWF0Y2hlcyB0aGUgY29sdW1uIG5hbWXCoGB3b3JkYMKgaW4gdGhlwqBgc3RvcF93b3Jkc2DCoGRhdGFzZXQgY29udGFpbmVkIGluIHRoZSBgdGlkeXRleHRgIHBhY2thZ2UuIFRoaXMgbWFrZXMgb3VyIGNhbGwgdG/CoGBhbnRpX2pvaW4oKWBzaW1wbGVyIGJlY2F1c2XCoGBhbnRpX2pvaW4oKWDCoGtub3dzIHRvIGxvb2sgZm9yIHRoZSBjb2x1bW4gbmFtZWTCoGB3b3JkYMKgaW4gZWFjaCBkYXRhc2V0LiBIb3dldmVyIHRoaXMgd2Fzbid0IHJlYWxseSBuZWNlc3Nhcnkgc2luY2UgYHdvcmRgIGlzIHRoZSBvbmx5IG1hdGNoaW5nIGNvbHVtbiBuYW1lIGluIGJvdGggZGF0YXNldHMgYW5kIGl0IHdvdWxkIGhhdmUgbWF0Y2hlZCB0aG9zZSBjb2x1bW5zIGJ5IGRlZmF1bHQuCgojIyMjIFtZb3VyIFR1cm5de3N0eWxlPSJjb2xvcjogZ3JlZW47In3CoOKktSB7c3R5bGU9ImZvbnQtc3R5bGU6IG5vcm1hbDsgZm9udC12YXJpYW50LWNhcHM6IG5vcm1hbDsgbGV0dGVyLXNwYWNpbmc6IG5vcm1hbDsgb3JwaGFuczogYXV0bzsgdGV4dC1hbGlnbjogc3RhcnQ7IHRleHQtaW5kZW50OiAwcHg7IHRleHQtdHJhbnNmb3JtOiBub25lOyB3aGl0ZS1zcGFjZTogbm9ybWFsOyB3aWRvd3M6IGF1dG87IHdvcmQtc3BhY2luZzogMHB4OyAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHJnYmEoMjYsIDI2LCAyNiwgMC4zKTsgLXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OiBhdXRvOyAtd2Via2l0LXRleHQtc3Ryb2tlLXdpZHRoOiAwcHg7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgY2FyZXQtY29sb3I6IHJnYigwLCAwLCAwKTsgY29sb3I6IHJnYigwLCAwLCAwKTsifQoKVXNlIHRoZSBjb2RlIGNodW5rIGJlbG93IHRvIHRha2UgYSBxdWljayBjb3VudCBvZiB0aGUgbW9zdCBjb21tb24gdG9rZW5zIGluIG91ciBgc3NfdHdlZXRzXzJgIGRhdGEgZnJhbWUgdG8gc2VlIGlmIHRoZSByZXN1bHRzIGFyZSBhIGxpdHRsZSBtb3JlIG1lYW5pbmdmdWwKCmBgYHtyfQpzc190b2tlbnNfMiAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKYGBgCgojIyMjIEN1c3RvbSBTdG9wIFdvcmRzCgpOb3RpY2UgdGhhdCB0aGUgbm9uc2Vuc2Ugd29yZCAiYW1wIiBpcyBhbW9uZyBvdXIgaGlnaCBmcmVxdWVuY3kgd29yZHMgYXMgd2VsbCBhcyBzb21lLiBXZSBjYW4gY3JlYXRlIG91ciBvd24gY3VzdG9tIHN0b3Agd29yZCBsaXN0IHRvIHRvIHdlZWQgb3V0IGFueSBhZGRpdGlvbmFsIHdvcmRzIHRoYXQgZG9uJ3QgY2FycnkgbXVjaCBtZWFuaW5nIGJ1dCBza2V3IG91ciBkYXRhIGJ5IGJlaW5nIHNvIHByb21pbmVudC4KCkxldCdzIGNyZWF0ZSBhIGN1c3RvbSBzdG9wIHdvcmQgbGlzdCBieSB1c2luZyB0aGUgc2ltcGxlIGBjKClgIGZ1bmN0aW9uIHRvIGNvbWJpbmUgb3VyIHdvcmRzLiBXZSBjYW4gdGhlIGFkZCBhIGZpbHRlciB0byBrZWVwIHJvd3Mgd2hlcmUgd29yZHMgaW4gb3VyIGB3b3JkYCBjb2x1bW4gZG8gTk9UIGAhYCBtYXRjaCB3b3JkcyBgJWluJWAgYG15X3N0b3B3b3Jkc2AgbGlzdDoKCmBgYHtyfQpteV9zdG9wd29yZHMgPC0gYygiYW1wIiwgIj0iLCAiKyIpCgpzc190b2tlbnNfMyA8LQogIHNzX3Rva2Vuc18yICU+JQogIGZpbHRlcighd29yZCAlaW4lIG15X3N0b3B3b3JkcykKYGBgCgpMZXQncyB0YWtlIGEgbG9vayBhdCBvdXIgdG9wIHdvcmRzIGFnYWluIGFuZCBzZWUgaWYgdGhhdCBkaWQgdGhlIHRyaWNrOgoKYGBge3J9CnNzX3Rva2Vuc18zICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQpgYGAKCk11Y2ggYmV0dGVyISBOb3RlIHRoYXQgd2UgY291bGQgZXh0ZW5kIHRoaXMgc3RvcCB3b3JkIGxpc3QgaW5kZWZpbml0ZWx5LiBGZWVsIGZyZWUgdG8gdXNlIHRoZSBjb2RlIGNodW5rIGJlbG93IHRvIHRyeSBhZGRpbmcgbW9yZSB3b3JkcyB0byBvdXIgc3RvcCBsaXN0LgoKQmVmb3JlIHdlIG1vdmUgYW55IGZ1cnRoZXIsIGxldCdzIHNhdmUgb3VyIHRpZGllZCB0d2VldHMgYXMgYSBuZXcgZGF0YSBmcmFtZSBmb3IgU2VjdGlvbiAzIGFuZCBhbHNvIHNhdmUgaXQgYXMgYSAuY3N2IGZpbGUgaW4gb3VyIGRhdGEgZm9sZGVyOgoKYGBge3J9CnNzX3RpZHlfdHdlZXRzIDwtIHNzX3Rva2Vuc18zCgp3cml0ZV9jc3Yoc3NfdG9rZW5zXzMsICJkYXRhL3NzX3RpZHlfdHdlZXRzLmNzdiIpCmBgYAoKIyMgMy4gRVhQTE9SRQoKQ2FsY3VsYXRpbmcgc3VtbWFyeSBzdGF0aXN0aWNzLCBkYXRhIHZpc3VhbGl6YXRpb24sIGFuZCBmZWF0dXJlIGVuZ2luZWVyaW5nICh0aGUgcHJvY2VzcyBvZiBjcmVhdGluZyBuZXcgdmFyaWFibGVzIGZyb20gYSBkYXRhc2V0KSBhcmUgYSBrZXkgcGFydCBvZiBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLiBJbiBTZWN0aW9uIDMsIHdlIGtlZXAgdGhpbmdzIHJlbGF0aXZlbHkgc2ltcGxlIGFuZCBmb2N1cyBvbiBzb21lIHNpbXBsZSBkYXRhIHN1bW1hcmllczoKCmEuICAqKldvcmQgQ291bnRzKiouIFdlIGZvY3VzIHByaW1hcmlseSBvbiB0aGUgdXNlIG9mIHdvcmQgY291bnRzIGFuZCBicmllZmx5IGludHJvZHVjZSB3b3JkIGZyZXF1ZW5jaWVzIHRvIGhlbHAgdXMgaWRlbnRpZnkgd29yZCBjb21tb25seSB1c2VkIGluIHR3ZWV0cyBhYm91dCB0aGUgTkdTUyBhbmQgQ0NTUyBjdXJyaWN1bHVtIHN0YW5kYXJkcy4KCmIuICAqKldvcmQgRnJlcXVlbmNpZXMqKi4gV2Ugd3JhcCB1cCB0aGlzIGxhYiBhbmQgcHJldmlldyBzb21lIGRhdGEgdmlzdWFsaXphdGlvbiB3b3JrIGluIGxhdGVyIGxhYnMgYnkgY3JlYXRpbmcgYSBzaW1wbGUgd29yZGNsb3VkIHRvIGV4cGxvcmUgc3VtbWFyaXplIGFuZCBoaWdobGlnaHQga2V5IHdvcmRzIGFtb25nIG91ciB0d2VldHMuCgojIyMgM2EuIFdvcmQgQ291bnRzCgpBcyBoaWdobGlnaHRlZCBpbsKgW1dvcmQgQ291bnRzIGFyZSBBbWF6aW5nXShodHRwczovL3RlZHVuZGVyd29vZC5jb20vMjAxMy8wMi8yMC93b3JkY291bnRzLWFyZS1hbWF6aW5nLyksIGFuIGV4Y2VsbGVudCBwb3N0IGFuZCBibG9nIGJ5IFRlZCBVbmRlcndvb2QgYXQgVW5pdmVyc2l0eSBvZiBJbGxpbm9pcywgb25lIHNpbXBsZSBidXQgcG93ZXJmdWwgYXBwcm9hY2ggdG8gdGV4dCBhbmFseXNpcyBpcyBjb3VudGluZyB0aGUgZnJlcXVlbmN5IGluIHdoaWNoIHdvcmRzIG9jY3VyIGluIGEgZ2l2ZW4gY29sbGVjdGlvbiBvZiBkb2N1bWVudHMsIG9yIGNvcnB1cy4KCldvcmQgY291bnRzIGFyZSBhIGdvb2QgZXhhbXBsZSBvZiBhIHNpbXBsZSBhcHByb2FjaCB0aGF0IGlsbHVzdHJhdGVzIHRoZSBjZW50cmFsIHF1ZXN0aW9uIHRvIHRleHQgbWluaW5nIGFuZCBuYXR1cmFsIGxhbmd1YWdlIHByb2Nlc3NpbmcsIGludHJvZHVjZWQgYXQgdGhlIGJlZ2lubmluZzoKCj4gSG93IGRvIHdlIHRvICoqcXVhbnRpZnkqKiB3aGF0IGEgZG9jdW1lbnQgb3IgY29sbGVjdGlvbiBvZiBkb2N1bWVudHMgaXMgYWJvdXQ/CgpTbyBmYXIsIHdlJ3ZlIHVzZWQgdGhlIGBjb3VudCgpYCBmdW5jdGlvbiBmcm9tIHRoZSB7ZHBseXJ9IHBhY2thZ2UgdG8gbG9vayBhdCB3b3JkIGNvdW50cyBhY3Jvc3Mgb3VyIGVudGlyZSBjb3JwdXMgb2YgdHdlZXRzLgoKTGV0J3MgdXNlIHRoZSBzYW1lIGZ1bmN0aW9uIHRvIGxvb2sgYXQgY291bnRzIG9mIHRoZSBtb3N0IGNvbW1vbiB3b3JkcyBieSBzdGFuZGFyZHMgdGhpcyB0aW1lIHNpbmNlIG9uZSBvZiBvdXIgZ29hbHMgaXMgdG8gY29tcGFyZSBwdWJsaWMgc2VudGltZW50IGJldHdlZW4gdGhlIHR3byBzdGFuZGFyZHM6CgpgYGB7cn0Kc3NfdGlkeV90d2VldHMgJT4lCiAgY291bnQoc3RhbmRhcmRzLCB3b3JkLCBzb3J0ID0gVFJVRSkKYGBgCgpOb3RlIHRoYXQgd2UgaW5jbHVkZWQgYHN0YW5kYXJkc2AgaW4gb3VyIGZ1bmN0aW9uIHRvIGNvdW50IGhvdyBvZnRlbiBlYWNoIHdvcmQgb2NjdXJzIGZvciBlYWNoIHNldCBvZiBzdGFuZGFyZHMuIEZvciBleGFtcGxlLCBpZiB5b3UgdGFiIHRocm91Z2ggdGhlIG91dHB1dCwgeW91IHdpbGwgc2VlIHRoYXQgInN0dWRlbnRzIiBpcyBhbW9uZyB0aGUgdG9wIHdvcmRzIGluIGJvdGggc2V0cyBvZiBzdGFuZGFyZHMsIGFuZCBvY2N1cnMgMSw0MzIgdGltZXMgaW4gdGhlIE5HU1MgdHdlZXRzIGFuZCAxLDEyNyB0aW1lcyBpbiB0aGUgQ0NTUyB0d2VldHMuCgpVbnN1cnByaXNpbmdseSB3b3JkcyBmcm9tIG91ciBUd2l0dGVyIEFQSSBzZWFyY2ggcXVlcnkgYXJlIGFtb25nIHRoZSB0b3Agd29yZHMgaW4gZWFjaCBzZXQgb2Ygc3RhbmRhcmRzIGFzIHdlbGwuCgpJdCdzIGEgbGl0dGxlIGRpZmZpY3VsdCB0byBkaXJlY3RseSBjb21wYXJlIHRoZSB0b3Agd29yZHMgaW4gZWFjaCBzZXQgc2luY2UgdGhleSBhcmUgbHVtcGVkIHRvZ2V0aGVyLiBMZXQncyB1c2Ugb3VyIGBmaWx0ZXIoKWAgZnVuY3Rpb24gYWdhaW4gdG8ganVzdCBsb29rIGF0IHRoZSBDQ1NTIHR3ZWV0cyBhbmQgc2F2ZSB0aGlzIGZvciBsYXRlciB0byB1c2UgaW4gb3VyIFJlYWNoIGFjdGl2aXR5OgoKYGBge3J9CmNjc3NfY291bnRzIDwtIHNzX3RpZHlfdHdlZXRzICU+JQogIGZpbHRlcihzdGFuZGFyZHMgPT0gImNjc3MiKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKCmNjc3NfY291bnRzCmBgYAoKIyMjIyBbWW91ciBUdXJuXXtzdHlsZT0iY29sb3I6IGdyZWVuOyJ9wqDipLUge3N0eWxlPSJmb250LXN0eWxlOiBub3JtYWw7IGZvbnQtdmFyaWFudC1jYXBzOiBub3JtYWw7IGxldHRlci1zcGFjaW5nOiBub3JtYWw7IG9ycGhhbnM6IGF1dG87IHRleHQtYWxpZ246IHN0YXJ0OyB0ZXh0LWluZGVudDogMHB4OyB0ZXh0LXRyYW5zZm9ybTogbm9uZTsgd2hpdGUtc3BhY2U6IG5vcm1hbDsgd2lkb3dzOiBhdXRvOyB3b3JkLXNwYWNpbmc6IDBweDsgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiByZ2JhKDI2LCAyNiwgMjYsIDAuMyk7IC13ZWJraXQtdGV4dC1zaXplLWFkanVzdDogYXV0bzsgLXdlYmtpdC10ZXh0LXN0cm9rZS13aWR0aDogMHB4OyB0ZXh0LWRlY29yYXRpb246IG5vbmU7IGNhcmV0LWNvbG9yOiByZ2IoMCwgMCwgMCk7IGNvbG9yOiByZ2IoMCwgMCwgMCk7In0KCk5vdyB1c2UgdGhlIGNvZGUgYmVsb3cgdG8gZ2V0IHRoZSBjb3VudHMgZm9yIG91ciBOR1NTIHR3ZWV0cyBzbyB3ZSBjYW4gY29tcGFyZSB0aGUgdG9wIHdvcmRzIGZvciBlYWNoIHNldCBvZiBzdGFuZGFyZHM6CgpgYGB7cn0KIyB5b3VyIGNvZGUgaGVyZQpgYGAKCldoYXQgbWlnaHQgdGhlIHRvcCB3b3JkcyBmb3IgZWFjaCBzZXQgb2Ygc3RhbmRhcmRzIHN1Z2dlc3QgYWJvdXQgc2ltaWxhcml0aWVzIGFuZCBkaWZmZXJlbmNlcyBmb3IgaG93IFR3aXR0ZXIgdXNlcnMgdGFsayBhYm91dCBlYWNoPyBXaGF0IG1pZ2h0IGl0IHN1Z2dlc3QgYWJvdXQgcHVibGljIHNlbnRpbWVudD8KCi0gICB5b3VyIHJlc3BvbnNlIGhlcmUKCiMjIyAzYi4gV29yZCBGcmVxdWVuY2llcwoKV2Ugc2F3IGFib3ZlIHRoYXQgdGhlIHdvcmQgInN0dWRlbnRzIiBpcyBhbW9uZyB0aGUgdG9wIHdvcmRzIGluIGJvdGggc2V0cyBvZiBzdGFuZGFyZHMsIGJ1dCB0byBoZWxwIGZhY2lsaXRhdGUgY29tcGFyaXNvbnMsIGlzIG9mdGVuIGhlbHBmdWwgdG8gbG9vayBhdCB0aGUgZnJlcXVlbmN5IHRoYXQgZWFjaCB3b3JkIG9jY3VycyBhbW9uZyBhbGwgd29yZHMgZm9yIHRoYXQgZG9jdW1lbnQgZ3JvdXAuIFRoaXMgd2lsbCBhbHNvIGhlbHBzIHVzIHRvIGJldHRlciBnYXVnZSBob3cgcHJvbWluZW50IGVhY2ggd29yZCBpcyBmb3IgZWFjaCBzZXQgb2Ygc3RhbmRhcmRzLgoKRm9yIGV4YW1wbGUsIGxldCdzIGNyZWF0ZSBjb3VudHMgZm9yIGVhY2jCoGBzdGFuZGFyZHNgwqBhbmTCoGB3b3JkYMKgcGFyaW5nIGxpa2Ugd2UgZGlkIGFib3ZlLCBhbmQgdGhlbiBjcmVhdGUgYSBuZXcgY29sdW1uIHVzaW5nIHRoZcKgYG11dGF0ZSgpYMKgZnVuY3Rpb24gdGhhdCBjYWxjdWxhdGVzIHRoZSBwcm9wb3J0aW9uIHRoYXQgd29yZCBtYWtlcyB1cCBhbW9uZyBhbGwgd29yZHM6CgpgYGB7cn0KY2Nzc19mcmVxdWVuY2llcyA8LSBjY3NzX2NvdW50cyAlPiUKICBtdXRhdGUocHJvcG9ydGlvbiA9IG4gLyBzdW0obikpCgpjY3NzX2ZyZXF1ZW5jaWVzCmBgYAoKIyMjIyBbWW91ciBUdXJuXXtzdHlsZT0iY29sb3I6IGdyZWVuOyJ9wqDipLUge3N0eWxlPSJmb250LXN0eWxlOiBub3JtYWw7IGZvbnQtdmFyaWFudC1jYXBzOiBub3JtYWw7IGxldHRlci1zcGFjaW5nOiBub3JtYWw7IG9ycGhhbnM6IGF1dG87IHRleHQtYWxpZ246IHN0YXJ0OyB0ZXh0LWluZGVudDogMHB4OyB0ZXh0LXRyYW5zZm9ybTogbm9uZTsgd2hpdGUtc3BhY2U6IG5vcm1hbDsgd2lkb3dzOiBhdXRvOyB3b3JkLXNwYWNpbmc6IDBweDsgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiByZ2JhKDI2LCAyNiwgMjYsIDAuMyk7IC13ZWJraXQtdGV4dC1zaXplLWFkanVzdDogYXV0bzsgLXdlYmtpdC10ZXh0LXN0cm9rZS13aWR0aDogMHB4OyB0ZXh0LWRlY29yYXRpb246IG5vbmU7IGNhcmV0LWNvbG9yOiByZ2IoMCwgMCwgMCk7IGNvbG9yOiByZ2IoMCwgMCwgMCk7In0KCk5vdyB1c2UgdGhlIGNvZGUgYmVsb3cgdG8gZ2V0IHRoZSBmcmVxdWVuY2llcyBmb3Igb3VyIE5HU1MgdHdlZXRzIHNvIHdlIGNhbiBjb21wYXJlIHRoZSB0b3Agd29yZHMgZm9yIGVhY2ggc2V0IG9mIHN0YW5kYXJkczoKCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCmBgYAoKV2UgY2FuIHNlZSBpbiBib3RoIGNhc2VzIHRoYXQgb3VyIHNlYXJjaCB0ZXJtcyBhcmUgaGVhdmlseSBza2V3aW5nIG91ciBwcm9wb3J0aW9ucy4gV2hhdCBtaWdodCB3ZSBkbyB0byBhZGRyZXNzIHRoaXM/CgotICAgeW91ciByZXNwb25zZSBoZXJlCgojIyA0LiBNT0RFTAoKQXMgaGlnaGxpZ2h0ZWQgaW7CoFtDaGFwdGVyIDMgb2YgRGF0YSBTY2llbmNlIGluIEVkdWNhdGlvbiBVc2luZyBSXShodHRwczovL2RhdGFzY2llbmNlaW5lZHVjYXRpb24uY29tL2MwMy5odG1sKSAsIHRoZcKgKipNb2RlbCoqwqBzdGVwIG9mIHRoZSBkYXRhIHNjaWVuY2UgcHJvY2VzcyBlbnRhaWxzICJ1c2luZyBzdGF0aXN0aWNhbCBtb2RlbHMsIGZyb20gc2ltcGxlIHRvIGNvbXBsZXgsIHRvIHVuZGVyc3RhbmQgdHJlbmRzIGFuZCBwYXR0ZXJucyBpbiB0aGUgZGF0YS4iIFRoZSBhdXRob3JzIG5vdGUgdGhhdCB3aGlsZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGFuZCBkYXRhIHZpc3VhbGl6YXRpb24gZHVyaW5nIHRoZcKgKipFeHBsb3JlKirCoHN0ZXAgY2FuIGhlbHAgdXMgdG8gaWRlbnRpZnkgcGF0dGVybnMgYW5kIHJlbGF0aW9uc2hpcHMgaW4gb3VyIGRhdGEsIHN0YXRpc3RpY2FsIG1vZGVscyBjYW4gYmUgdXNlZCB0byBoZWxwIHVzIGRldGVybWluZSBpZiByZWxhdGlvbnNoaXBzLCBwYXR0ZXJucyBhbmQgdHJlbmRzIGFyZSBhY3R1YWxseSBtZWFuaW5nZnVsLiBJbiBUTSBMZWFybmluZyBMYWIgMywgd2UnbGwgdGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZSBzdHVkeSBieSBAcm9zZW5iZXJnMjAyMCBhbmQgaG93IHRoZXkgdXNlZCBtb2RlbGluZyB0byBjb21wYXJlIGRpZmZlcmVuY2VzIGluIHNlbnRpbWVudCBiZXR3ZWVuIHRlYWNoZXJzIGFuZCBub24tdGVhY2hlcnMgZGlzY3Vzc2luZyB0aGUgY29tbW9uIGNvcmUuCgojIyA1LiBDT01NVU5JQ0FURQoKQ29uZ3JhdHVsYXRpb25zIC0geW91J3ZlIGNvbXBsZXRlZCB0aGUgZmlyc3QgdGV4dCBtaW5pbmcgbGVhcm5pbmcgbGFiISBUbyBjb21wbGV0ZSB5b3VyIHdvcmssIHlvdSBjYW4gY2xpY2sgdGhlIGRyb3AgZG93biBhcnJvdyBhdCB0aGUgdG9wIG9mIHRoZSBmaWxlLCB0aGVuIHNlbGVjdCAiS25pdCB0b3AgSFRNTCIuIFRoaXMgd2lsbCBjcmVhdGUgYSByZXBvcnQgaW4geW91ciBGaWxlcyBwYW5lIHRoYXQgc2VydmVzIGFzIGEgcmVjb3JkIG9mIHlvdXIgY29kZSBhbmQgaXRzIG91dHB1dCB5b3UgY2FuIG9wZW4gb3Igc2hhcmUuCgpJZiB5b3Ugd2FudGVkLCB5b3UgY291bGQgc2F2ZSB0aGUgcHJvY2Vzc2VkIGRhdGEgc2V0IHRvIHlvdXIgZGF0YSBmb2xkZXIuIFRoZSBgd3JpdGVfY3N2KClgIGZ1bmN0aW9uIGlzIHVzZWZ1bCBmb3IgdGhpcy4gVGhlIGZvbGxvd2luZyBjb2RlIGlzIHNldCB0byBub3QgcnVuLCBhcyB3ZSB3YW50ZWQgdG8gZW5zdXJlIHRoYXQgZXZlcnlvbmUgaGFkIHRoZSBkYXRhIHNldCBuZWVkZWQgdG8gYmVnaW4gdGhlIHNlY29uZCBsZWFybmluZyBsYWIsIGJ1dCBpZiB5b3UncmUgY29uZmlkZW50IGluIHlvdXIgcHJlcGFyZWQgZGF0YSwgeW91IGNhbiBzYXZlIGl0IHdpdGggdGhlIGZvbGxvd2luZzoKCmBgYHtyLCBldmFsID0gRkFMU0V9CndyaXRlX2NzdigpCmBgYAoKIyMgUmVhY2ggKE9wdGlvbmFsKQoKSWYgeW91J3JlIHVzaW5nIGRhdGEgdGhhdCB5b3UgYnJvdWdodCB0byB0aGUgaW5zdGl0dXRlIG9yIGRhdGEgdGhhdCB5b3UgcHVsbGVkIGZyb20gVHdpdHRlciwgdHJ5IHRpZHlpbmcgeW91ciBkYXRhIGludG8gYSB0aWR5IHRleHQgZm9ybWF0IGFuZCBleGFtaW5pbmcgdGhlIHRvcCB3b3JkcyBpbiB5b3VyIGRhdGFzZXQuCgpJZiB5b3UnZCBsaWtlIHRvIHVzZSB0aGUgZGF0YSB3ZSd2ZSBiZWVuIHdvcmtpbmcgd2l0aCBmb3IgeW91ciByZWFjaCwgbGV0J3MgdHJ5IHNvbWUgYmFzaWMgZGF0YSB2aXN1YWxpemF0aW9uIHdpdGggdGV4dC4gVGhlwqBgd29yZGNsb3VkMmDCoHBhY2thZ2UgaXMgcHJldHR5IGRlYWQgc2ltcGxlIHRvb2wgZm9yIGdlbmVyYXRpbmcgSFRNTCBiYXNlZCB3b3JkIGNsb3Vkcy4KCkZvciBleGFtcGxlLCBsZXQncyBsb2FkIHRoZcKgYHdvcmRjbG91ZHMyYMKgbGlicmFyeSwgYW5kIHJ1biB0aGUgYHdvcmRjbG91ZDIoKWAgZnVuY3Rpb24gb24gb3VyIGBjY3NzX2NvdW50c2AgZGF0YSBmcmFtZToKCmBgYHtyfQpsaWJyYXJ5KHdvcmRjbG91ZDIpCgp3b3JkY2xvdWQyKGNjc3NfY291bnRzKQpgYGAKCkFzIHlvdSBjYW4gc2VlLCAibWF0aCIgaXMgYSBwcmV0dHkgY29tbW9uIHRvcGljIHdpdGggZGlzY3Vzc2luZyB0aGUgY29tbW9uIGNvcmUgb24gdHdpdHRlciBidXQgd29yZHMgbGlrZSAiY29yZSIgYW5kICJjb21tb24iIGFyZSBub3QgdmVyeSBoZWxwZnVsIHNpbmNlIHRob3NlIHdlcmUgaW4gb3VyIHNlYXJjaCB0ZXJtcyB3aGVuIHB1bGxpbmcgZGF0YSBmcm9tIFR3aXR0ZXIuCgpJbiBhIHNlcGFyYXRlIHIgc2NyaXB0IGZpbGUsIHRyeSBtb2RpZnlpbmcgb3VyIGxpc3Qgb2Ygc3RvcCB3b3JkcywgcmV0aWR5aW5nIG91ciB0ZXh0LCBhbmQgZG9pbmcgYSBuZXcgd29yZCBjb3VudCB3aXRoIHRoZXNlIGFuZCBwZXJoYXBzIG90aGVyIHdvcmRzIHJlbW92ZWQuIEFsc28sIHRha2UgYSBsb29rIGF0IHRoZSBoZWxwIGZpbGUgZm9yIGB3b3JkY2xvdWRzMmAgdG8gc2VlIGlmIHRoZXJlIG1pZ2h0IGJlIG90aGVyd2lzZSB5b3UgY291bGQgdmlzdWFsbHkgaW1wcm92ZSB0aGlzIHZpc3VhbGl6YXRpb24uCgpXb3JkIGNsb3VkcyBhcmUgbXVjaCBtYWxpZ25lZCBhbmQgc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzIHBpZSBjaGFydHMgZm9yIHdvcmRzLCBidXQgdGhleSBjYW4gYmUgdXNlZnVsIGZvciBxdWlja2x5IHN1bW1hcml6aW5nIGFuZCBjb21tdW5pY2F0aW5nIHF1YWxpdGF0aXZlIGRhdGEgZm9yIGVkdWNhdGlvbiBwcmFjdGl0aW9uZXJzIGFuZCBhcmUgaW50dWl0aXZlIGZvciB0aGVtIHRvIGludGVycHJldC4gQWxzbywgZm9yIGJldHRlciBvciB3b3JzZSwgdGhlc2UgYXJlIG5vdyBpbmNsdWRlZCBhcyBhIGRlZmF1bHQgdml6IGZvciBvcGVuLWVuZGVkIHN1cnZleSBpdGVtcyBpbiBvbmxpbmUgUXVhbHRyaWNzIHJlcG9ydHMuCgojIyBSZWZsZWN0aW9uCgpJbiB0aGlzIGxlYXJuaW5nIGxhYiwgd2UgZm9jdXNlZCBvbiB0aGUgbGl0ZXJhdHVyZSBndWlkaW5nIG91ciBhbmFseXNpczsgd3JhbmdsaW5nIG91ciBkYXRhIGludG8gYSBvbmUtdG9rZW4tcGVyLXJvdyB0aWR5IHRleHQgZm9ybWF0OyBhbmQgdXNpbmcgc2ltcGxlIHdvcmQgY291bnRzIGFuZCBmcmVxdWVuY2llcyB0byBjb21wYXJlIGNvbW1vbiB3b3JkcyB1c2VkIGluIHR3ZWV0cyBhYm91dCB0aGUgTkdTUyBhbmQgQ0NTUyBjdXJyaWN1bHVtIHN0YW5kYXJkcy4gQmVsb3csIGFkZCBhIGZldyBub3RlcyBpbiByZXNwb25zZSB0byB0aGUgZm9sbG93aW5nIHByb21wdHM6CgoxLiAgT25lIHRoaW5nIEkgdG9vayBhd2F5IGZyb20gdGhpcyBsZWFybmluZyBsYWI6CgogICAgLSAgIAoKMi4gIE9uZSB0aGluZyBJIHdhbnQgdG8gbGVhcm4gbW9yZSBhYm91dDoKCiAgICAtICAgCgojIyBSZWZlcmVuY2VzCgpOb3RlOiBDaXRhdGlvbnMgZW1iZWRkZWQgaW4gUiBNYXJrZG93biB3aWxsIG9ubHkgc2hvdyB1cG9uIGtuaXR0aW5nLgo=